blob: 10ea0583a5913e20beeffb938fd14cea18eb85c5 [file] [log] [blame]
sewardje663cb92002-04-12 10:26:32 +00001
2/*--------------------------------------------------------------------*/
3/*--- A user-space pthreads implementation. vg_scheduler.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, an x86 protected-mode emulator
8 designed for debugging and profiling binaries on x86-Unixes.
9
10 Copyright (C) 2000-2002 Julian Seward
11 jseward@acm.org
sewardje663cb92002-04-12 10:26:32 +000012
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file LICENSE.
29*/
30
31#include "vg_include.h"
32#include "vg_constants.h"
33
34#include "valgrind.h" /* for VG_USERREQ__MAKE_NOACCESS and
35 VG_USERREQ__DO_LEAK_CHECK */
36
sewardj77e466c2002-04-14 02:29:29 +000037/* BORKAGE/ISSUES as of 14 Apr 02
sewardje663cb92002-04-12 10:26:32 +000038
sewardj77e466c2002-04-14 02:29:29 +000039Note! This pthreads implementation is so poor as to not be
40suitable for use by anyone at all!
sewardje663cb92002-04-12 10:26:32 +000041
sewardj77e466c2002-04-14 02:29:29 +000042- Currently, when a signal is run, just the ThreadStatus.status fields
43 are saved in the signal frame, along with the CPU state. Question:
44 should I also save and restore:
45 ThreadStatus.joiner
46 ThreadStatus.waited_on_mid
47 ThreadStatus.awaken_at
48 ThreadStatus.retval
49 Currently unsure, and so am not doing so.
sewardje663cb92002-04-12 10:26:32 +000050
sewardj77e466c2002-04-14 02:29:29 +000051- Signals interrupting read/write and nanosleep: SA_RESTART settings.
52 Read/write correctly return with EINTR when SA_RESTART isn't
53 specified and they are interrupted by a signal. nanosleep just
54 pretends signals don't exist -- should be fixed.
sewardje663cb92002-04-12 10:26:32 +000055
sewardj75fe1892002-04-14 02:46:33 +000056- Read/write syscall starts: don't crap out when the initial
57 nonblocking read/write returns an error.
sewardj8937c812002-04-12 20:12:20 +000058
sewardj9a199dc2002-04-14 13:01:38 +000059- Get rid of restrictions re use of sigaltstack; they are no longer
60 needed.
61
sewardj6072c362002-04-19 14:40:57 +000062- Fix signals properly, so that each thread has its own blocking mask.
63 Currently this isn't done, and (worse?) signals are delivered to
64 Thread 1 (the root thread) regardless.
65
66 So, what's the deal with signals and mutexes? If a thread is
67 blocked on a mutex, or for a condition variable for that matter, can
68 signals still be delivered to it? This has serious consequences --
69 deadlocks, etc.
70
sewardje462e202002-04-13 04:09:07 +000071*/
sewardje663cb92002-04-12 10:26:32 +000072
73
74/* ---------------------------------------------------------------------
75 Types and globals for the scheduler.
76 ------------------------------------------------------------------ */
77
78/* type ThreadId is defined in vg_include.h. */
79
80/* struct ThreadState is defined in vg_include.h. */
81
sewardj6072c362002-04-19 14:40:57 +000082/* Private globals. A statically allocated array of threads. NOTE:
83 [0] is never used, to simplify the simulation of initialisers for
84 LinuxThreads. */
sewardje663cb92002-04-12 10:26:32 +000085static ThreadState vg_threads[VG_N_THREADS];
86
sewardj1e8cdc92002-04-18 11:37:52 +000087/* The tid of the thread currently in VG_(baseBlock). */
88static Int vg_tid_currently_in_baseBlock = VG_INVALID_THREADID;
89
sewardje663cb92002-04-12 10:26:32 +000090
91/* vg_oursignalhandler() might longjmp(). Here's the jmp_buf. */
92jmp_buf VG_(scheduler_jmpbuf);
93/* ... and if so, here's the signal which caused it to do so. */
94Int VG_(longjmpd_on_signal);
95
96
97/* Machinery to keep track of which threads are waiting on which
98 fds. */
99typedef
100 struct {
101 /* The thread which made the request. */
102 ThreadId tid;
103
104 /* The next two fields describe the request. */
105 /* File descriptor waited for. -1 means this slot is not in use */
106 Int fd;
107 /* The syscall number the fd is used in. */
108 Int syscall_no;
109
110 /* False => still waiting for select to tell us the fd is ready
111 to go. True => the fd is ready, but the results have not yet
112 been delivered back to the calling thread. Once the latter
113 happens, this entire record is marked as no longer in use, by
114 making the fd field be -1. */
115 Bool ready;
116 }
117 VgWaitedOnFd;
118
119static VgWaitedOnFd vg_waiting_fds[VG_N_WAITING_FDS];
120
121
sewardj5f07b662002-04-23 16:52:51 +0000122/* Keeping track of keys. */
123typedef
124 struct {
125 /* Has this key been allocated ? */
126 Bool inuse;
127 /* If .inuse==True, records the address of the associated
128 destructor, or NULL if none. */
129 void (*destructor)(void*);
130 }
131 ThreadKeyState;
132
133/* And our array of thread keys. */
134static ThreadKeyState vg_thread_keys[VG_N_THREAD_KEYS];
135
136typedef UInt ThreadKey;
137
138
sewardje663cb92002-04-12 10:26:32 +0000139/* Forwards */
sewardj5f07b662002-04-23 16:52:51 +0000140static void do_pthread_cond_timedwait_TIMEOUT ( ThreadId tid );
141
sewardje663cb92002-04-12 10:26:32 +0000142static void do_nontrivial_clientreq ( ThreadId tid );
143
sewardj6072c362002-04-19 14:40:57 +0000144static void scheduler_sanity ( void );
145
sewardjd7fd4d22002-04-24 01:57:27 +0000146static void do_pthread_mutex_unlock ( ThreadId,
147 void* /* pthread_cond_t* */ );
148static void do_pthread_mutex_lock ( ThreadId, Bool,
149 void* /* pthread_cond_t* */ );
150
sewardj51c0aaf2002-04-25 01:32:10 +0000151static void do_pthread_getspecific ( ThreadId,
152 UInt /* pthread_key_t */ );
153
sewardje663cb92002-04-12 10:26:32 +0000154
155/* ---------------------------------------------------------------------
156 Helper functions for the scheduler.
157 ------------------------------------------------------------------ */
158
sewardj604ec3c2002-04-18 22:38:41 +0000159static __inline__
160Bool is_valid_tid ( ThreadId tid )
161{
162 /* tid is unsigned, hence no < 0 test. */
sewardj6072c362002-04-19 14:40:57 +0000163 if (tid == 0) return False;
sewardj604ec3c2002-04-18 22:38:41 +0000164 if (tid >= VG_N_THREADS) return False;
sewardj604ec3c2002-04-18 22:38:41 +0000165 return True;
166}
167
168
sewardj1e8cdc92002-04-18 11:37:52 +0000169/* For constructing error messages only: try and identify a thread
170 whose stack this address currently falls within, or return
171 VG_INVALID_THREADID if it doesn't. A small complication is dealing
172 with any currently VG_(baseBlock)-resident thread.
173*/
174ThreadId VG_(identify_stack_addr)( Addr a )
175{
176 ThreadId tid, tid_to_skip;
177
178 tid_to_skip = VG_INVALID_THREADID;
179
180 /* First check to see if there's a currently-loaded thread in
181 VG_(baseBlock). */
182 if (vg_tid_currently_in_baseBlock != VG_INVALID_THREADID) {
183 tid = vg_tid_currently_in_baseBlock;
184 if (VG_(baseBlock)[VGOFF_(m_esp)] <= a
185 && a <= vg_threads[tid].stack_highest_word)
186 return tid;
187 else
188 tid_to_skip = tid;
189 }
190
sewardj6072c362002-04-19 14:40:57 +0000191 for (tid = 1; tid < VG_N_THREADS; tid++) {
sewardj1e8cdc92002-04-18 11:37:52 +0000192 if (vg_threads[tid].status == VgTs_Empty) continue;
193 if (tid == tid_to_skip) continue;
194 if (vg_threads[tid].m_esp <= a
195 && a <= vg_threads[tid].stack_highest_word)
196 return tid;
197 }
198 return VG_INVALID_THREADID;
199}
200
201
sewardj15a43e12002-04-17 19:35:12 +0000202/* Print the scheduler status. */
203void VG_(pp_sched_status) ( void )
sewardje663cb92002-04-12 10:26:32 +0000204{
205 Int i;
206 VG_(printf)("\nsched status:\n");
sewardj6072c362002-04-19 14:40:57 +0000207 for (i = 1; i < VG_N_THREADS; i++) {
sewardje663cb92002-04-12 10:26:32 +0000208 if (vg_threads[i].status == VgTs_Empty) continue;
sewardj15a43e12002-04-17 19:35:12 +0000209 VG_(printf)("\nThread %d: status = ", i);
sewardje663cb92002-04-12 10:26:32 +0000210 switch (vg_threads[i].status) {
sewardj6072c362002-04-19 14:40:57 +0000211 case VgTs_Runnable: VG_(printf)("Runnable"); break;
212 case VgTs_WaitFD: VG_(printf)("WaitFD"); break;
213 case VgTs_WaitJoiner: VG_(printf)("WaitJoiner(%d)",
sewardje663cb92002-04-12 10:26:32 +0000214 vg_threads[i].joiner); break;
sewardj6072c362002-04-19 14:40:57 +0000215 case VgTs_WaitJoinee: VG_(printf)("WaitJoinee"); break;
216 case VgTs_Sleeping: VG_(printf)("Sleeping"); break;
217 case VgTs_WaitMX: VG_(printf)("WaitMX"); break;
sewardj3b5d8862002-04-20 13:53:23 +0000218 case VgTs_WaitCV: VG_(printf)("WaitCV"); break;
sewardje663cb92002-04-12 10:26:32 +0000219 default: VG_(printf)("???"); break;
220 }
sewardj3b5d8862002-04-20 13:53:23 +0000221 VG_(printf)(", associated_mx = %p, associated_cv = %p\n",
222 vg_threads[i].associated_mx,
223 vg_threads[i].associated_cv );
sewardj15a43e12002-04-17 19:35:12 +0000224 VG_(pp_ExeContext)(
225 VG_(get_ExeContext)( False, vg_threads[i].m_eip,
226 vg_threads[i].m_ebp ));
sewardje663cb92002-04-12 10:26:32 +0000227 }
228 VG_(printf)("\n");
229}
230
231static
232void add_waiting_fd ( ThreadId tid, Int fd, Int syscall_no )
233{
234 Int i;
235
236 vg_assert(fd != -1); /* avoid total chaos */
237
238 for (i = 0; i < VG_N_WAITING_FDS; i++)
239 if (vg_waiting_fds[i].fd == -1)
240 break;
241
242 if (i == VG_N_WAITING_FDS)
243 VG_(panic)("add_waiting_fd: VG_N_WAITING_FDS is too low");
244 /*
245 VG_(printf)("add_waiting_fd: add (tid %d, fd %d) at slot %d\n",
246 tid, fd, i);
247 */
248 vg_waiting_fds[i].fd = fd;
249 vg_waiting_fds[i].tid = tid;
250 vg_waiting_fds[i].ready = False;
251 vg_waiting_fds[i].syscall_no = syscall_no;
252}
253
254
255
256static
257void print_sched_event ( ThreadId tid, Char* what )
258{
sewardj45b4b372002-04-16 22:50:32 +0000259 VG_(message)(Vg_DebugMsg, " SCHED[%d]: %s", tid, what );
sewardj8937c812002-04-12 20:12:20 +0000260}
261
262
263static
264void print_pthread_event ( ThreadId tid, Char* what )
265{
266 VG_(message)(Vg_DebugMsg, "PTHREAD[%d]: %s", tid, what );
sewardje663cb92002-04-12 10:26:32 +0000267}
268
269
270static
271Char* name_of_sched_event ( UInt event )
272{
273 switch (event) {
sewardje663cb92002-04-12 10:26:32 +0000274 case VG_TRC_EBP_JMP_SYSCALL: return "SYSCALL";
275 case VG_TRC_EBP_JMP_CLIENTREQ: return "CLIENTREQ";
276 case VG_TRC_INNER_COUNTERZERO: return "COUNTERZERO";
277 case VG_TRC_INNER_FASTMISS: return "FASTMISS";
278 case VG_TRC_UNRESUMABLE_SIGNAL: return "FATALSIGNAL";
279 default: return "??UNKNOWN??";
280 }
281}
282
283
284/* Create a translation of the client basic block beginning at
285 orig_addr, and add it to the translation cache & translation table.
286 This probably doesn't really belong here, but, hey ...
287*/
sewardj1e8cdc92002-04-18 11:37:52 +0000288static
289void create_translation_for ( ThreadId tid, Addr orig_addr )
sewardje663cb92002-04-12 10:26:32 +0000290{
291 Addr trans_addr;
292 TTEntry tte;
293 Int orig_size, trans_size;
294 /* Ensure there is space to hold a translation. */
295 VG_(maybe_do_lru_pass)();
sewardj1e8cdc92002-04-18 11:37:52 +0000296 VG_(translate)( &vg_threads[tid],
297 orig_addr, &orig_size, &trans_addr, &trans_size );
sewardje663cb92002-04-12 10:26:32 +0000298 /* Copy data at trans_addr into the translation cache.
299 Returned pointer is to the code, not to the 4-byte
300 header. */
301 /* Since the .orig_size and .trans_size fields are
302 UShort, be paranoid. */
303 vg_assert(orig_size > 0 && orig_size < 65536);
304 vg_assert(trans_size > 0 && trans_size < 65536);
305 tte.orig_size = orig_size;
306 tte.orig_addr = orig_addr;
307 tte.trans_size = trans_size;
308 tte.trans_addr = VG_(copy_to_transcache)
309 ( trans_addr, trans_size );
310 tte.mru_epoch = VG_(current_epoch);
311 /* Free the intermediary -- was allocated by VG_(emit_code). */
312 VG_(jitfree)( (void*)trans_addr );
313 /* Add to trans tab and set back pointer. */
314 VG_(add_to_trans_tab) ( &tte );
315 /* Update stats. */
316 VG_(this_epoch_in_count) ++;
317 VG_(this_epoch_in_osize) += orig_size;
318 VG_(this_epoch_in_tsize) += trans_size;
319 VG_(overall_in_count) ++;
320 VG_(overall_in_osize) += orig_size;
321 VG_(overall_in_tsize) += trans_size;
322 /* Record translated area for SMC detection. */
323 VG_(smc_mark_original) ( orig_addr, orig_size );
324}
325
326
327/* Allocate a completely empty ThreadState record. */
328static
329ThreadId vg_alloc_ThreadState ( void )
330{
331 Int i;
sewardj6072c362002-04-19 14:40:57 +0000332 for (i = 1; i < VG_N_THREADS; i++) {
sewardje663cb92002-04-12 10:26:32 +0000333 if (vg_threads[i].status == VgTs_Empty)
334 return i;
335 }
336 VG_(printf)("vg_alloc_ThreadState: no free slots available\n");
337 VG_(printf)("Increase VG_N_THREADS, rebuild and try again.\n");
338 VG_(panic)("VG_N_THREADS is too low");
339 /*NOTREACHED*/
340}
341
342
343ThreadState* VG_(get_thread_state) ( ThreadId tid )
344{
sewardj6072c362002-04-19 14:40:57 +0000345 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +0000346 vg_assert(vg_threads[tid].status != VgTs_Empty);
347 return & vg_threads[tid];
348}
349
350
sewardj1e8cdc92002-04-18 11:37:52 +0000351ThreadState* VG_(get_current_thread_state) ( void )
352{
353 vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID);
sewardj6072c362002-04-19 14:40:57 +0000354 return VG_(get_thread_state) ( vg_tid_currently_in_baseBlock );
sewardj1e8cdc92002-04-18 11:37:52 +0000355}
356
357
358ThreadId VG_(get_current_tid) ( void )
359{
360 vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID);
361 return vg_tid_currently_in_baseBlock;
362}
363
364
sewardje663cb92002-04-12 10:26:32 +0000365/* Copy the saved state of a thread into VG_(baseBlock), ready for it
366 to be run. */
367__inline__
368void VG_(load_thread_state) ( ThreadId tid )
369{
370 Int i;
sewardj1e8cdc92002-04-18 11:37:52 +0000371 vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
372
sewardje663cb92002-04-12 10:26:32 +0000373 VG_(baseBlock)[VGOFF_(m_eax)] = vg_threads[tid].m_eax;
374 VG_(baseBlock)[VGOFF_(m_ebx)] = vg_threads[tid].m_ebx;
375 VG_(baseBlock)[VGOFF_(m_ecx)] = vg_threads[tid].m_ecx;
376 VG_(baseBlock)[VGOFF_(m_edx)] = vg_threads[tid].m_edx;
377 VG_(baseBlock)[VGOFF_(m_esi)] = vg_threads[tid].m_esi;
378 VG_(baseBlock)[VGOFF_(m_edi)] = vg_threads[tid].m_edi;
379 VG_(baseBlock)[VGOFF_(m_ebp)] = vg_threads[tid].m_ebp;
380 VG_(baseBlock)[VGOFF_(m_esp)] = vg_threads[tid].m_esp;
381 VG_(baseBlock)[VGOFF_(m_eflags)] = vg_threads[tid].m_eflags;
382 VG_(baseBlock)[VGOFF_(m_eip)] = vg_threads[tid].m_eip;
383
384 for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
385 VG_(baseBlock)[VGOFF_(m_fpustate) + i] = vg_threads[tid].m_fpu[i];
386
387 VG_(baseBlock)[VGOFF_(sh_eax)] = vg_threads[tid].sh_eax;
388 VG_(baseBlock)[VGOFF_(sh_ebx)] = vg_threads[tid].sh_ebx;
389 VG_(baseBlock)[VGOFF_(sh_ecx)] = vg_threads[tid].sh_ecx;
390 VG_(baseBlock)[VGOFF_(sh_edx)] = vg_threads[tid].sh_edx;
391 VG_(baseBlock)[VGOFF_(sh_esi)] = vg_threads[tid].sh_esi;
392 VG_(baseBlock)[VGOFF_(sh_edi)] = vg_threads[tid].sh_edi;
393 VG_(baseBlock)[VGOFF_(sh_ebp)] = vg_threads[tid].sh_ebp;
394 VG_(baseBlock)[VGOFF_(sh_esp)] = vg_threads[tid].sh_esp;
395 VG_(baseBlock)[VGOFF_(sh_eflags)] = vg_threads[tid].sh_eflags;
sewardj1e8cdc92002-04-18 11:37:52 +0000396
397 vg_tid_currently_in_baseBlock = tid;
sewardje663cb92002-04-12 10:26:32 +0000398}
399
400
401/* Copy the state of a thread from VG_(baseBlock), presumably after it
402 has been descheduled. For sanity-check purposes, fill the vacated
403 VG_(baseBlock) with garbage so as to make the system more likely to
404 fail quickly if we erroneously continue to poke around inside
405 VG_(baseBlock) without first doing a load_thread_state().
406*/
407__inline__
408void VG_(save_thread_state) ( ThreadId tid )
409{
410 Int i;
411 const UInt junk = 0xDEADBEEF;
412
sewardj1e8cdc92002-04-18 11:37:52 +0000413 vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID);
414
sewardje663cb92002-04-12 10:26:32 +0000415 vg_threads[tid].m_eax = VG_(baseBlock)[VGOFF_(m_eax)];
416 vg_threads[tid].m_ebx = VG_(baseBlock)[VGOFF_(m_ebx)];
417 vg_threads[tid].m_ecx = VG_(baseBlock)[VGOFF_(m_ecx)];
418 vg_threads[tid].m_edx = VG_(baseBlock)[VGOFF_(m_edx)];
419 vg_threads[tid].m_esi = VG_(baseBlock)[VGOFF_(m_esi)];
420 vg_threads[tid].m_edi = VG_(baseBlock)[VGOFF_(m_edi)];
421 vg_threads[tid].m_ebp = VG_(baseBlock)[VGOFF_(m_ebp)];
422 vg_threads[tid].m_esp = VG_(baseBlock)[VGOFF_(m_esp)];
423 vg_threads[tid].m_eflags = VG_(baseBlock)[VGOFF_(m_eflags)];
424 vg_threads[tid].m_eip = VG_(baseBlock)[VGOFF_(m_eip)];
425
426 for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
427 vg_threads[tid].m_fpu[i] = VG_(baseBlock)[VGOFF_(m_fpustate) + i];
428
429 vg_threads[tid].sh_eax = VG_(baseBlock)[VGOFF_(sh_eax)];
430 vg_threads[tid].sh_ebx = VG_(baseBlock)[VGOFF_(sh_ebx)];
431 vg_threads[tid].sh_ecx = VG_(baseBlock)[VGOFF_(sh_ecx)];
432 vg_threads[tid].sh_edx = VG_(baseBlock)[VGOFF_(sh_edx)];
433 vg_threads[tid].sh_esi = VG_(baseBlock)[VGOFF_(sh_esi)];
434 vg_threads[tid].sh_edi = VG_(baseBlock)[VGOFF_(sh_edi)];
435 vg_threads[tid].sh_ebp = VG_(baseBlock)[VGOFF_(sh_ebp)];
436 vg_threads[tid].sh_esp = VG_(baseBlock)[VGOFF_(sh_esp)];
437 vg_threads[tid].sh_eflags = VG_(baseBlock)[VGOFF_(sh_eflags)];
438
439 /* Fill it up with junk. */
440 VG_(baseBlock)[VGOFF_(m_eax)] = junk;
441 VG_(baseBlock)[VGOFF_(m_ebx)] = junk;
442 VG_(baseBlock)[VGOFF_(m_ecx)] = junk;
443 VG_(baseBlock)[VGOFF_(m_edx)] = junk;
444 VG_(baseBlock)[VGOFF_(m_esi)] = junk;
445 VG_(baseBlock)[VGOFF_(m_edi)] = junk;
446 VG_(baseBlock)[VGOFF_(m_ebp)] = junk;
447 VG_(baseBlock)[VGOFF_(m_esp)] = junk;
448 VG_(baseBlock)[VGOFF_(m_eflags)] = junk;
449 VG_(baseBlock)[VGOFF_(m_eip)] = junk;
450
451 for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
452 VG_(baseBlock)[VGOFF_(m_fpustate) + i] = junk;
sewardj1e8cdc92002-04-18 11:37:52 +0000453
454 vg_tid_currently_in_baseBlock = VG_INVALID_THREADID;
sewardje663cb92002-04-12 10:26:32 +0000455}
456
457
458/* Run the thread tid for a while, and return a VG_TRC_* value to the
459 scheduler indicating what happened. */
sewardj6072c362002-04-19 14:40:57 +0000460static
sewardje663cb92002-04-12 10:26:32 +0000461UInt run_thread_for_a_while ( ThreadId tid )
462{
sewardj7ccc5c22002-04-24 21:39:11 +0000463 volatile UInt trc = 0;
sewardj6072c362002-04-19 14:40:57 +0000464 vg_assert(is_valid_tid(tid));
465 vg_assert(vg_threads[tid].status == VgTs_Runnable);
sewardje663cb92002-04-12 10:26:32 +0000466 vg_assert(VG_(bbs_to_go) > 0);
467
468 VG_(load_thread_state) ( tid );
469 if (__builtin_setjmp(VG_(scheduler_jmpbuf)) == 0) {
470 /* try this ... */
471 trc = VG_(run_innerloop)();
472 /* We get here if the client didn't take a fault. */
473 } else {
474 /* We get here if the client took a fault, which caused our
475 signal handler to longjmp. */
476 vg_assert(trc == 0);
477 trc = VG_TRC_UNRESUMABLE_SIGNAL;
478 }
479 VG_(save_thread_state) ( tid );
480 return trc;
481}
482
483
484/* Increment the LRU epoch counter. */
485static
486void increment_epoch ( void )
487{
488 VG_(current_epoch)++;
489 if (VG_(clo_verbosity) > 2) {
490 UInt tt_used, tc_used;
491 VG_(get_tt_tc_used) ( &tt_used, &tc_used );
492 VG_(message)(Vg_UserMsg,
493 "%lu bbs, in: %d (%d -> %d), out %d (%d -> %d), TT %d, TC %d",
494 VG_(bbs_done),
495 VG_(this_epoch_in_count),
496 VG_(this_epoch_in_osize),
497 VG_(this_epoch_in_tsize),
498 VG_(this_epoch_out_count),
499 VG_(this_epoch_out_osize),
500 VG_(this_epoch_out_tsize),
501 tt_used, tc_used
502 );
503 }
504 VG_(this_epoch_in_count) = 0;
505 VG_(this_epoch_in_osize) = 0;
506 VG_(this_epoch_in_tsize) = 0;
507 VG_(this_epoch_out_count) = 0;
508 VG_(this_epoch_out_osize) = 0;
509 VG_(this_epoch_out_tsize) = 0;
510}
511
512
513/* Initialise the scheduler. Create a single "main" thread ready to
sewardj6072c362002-04-19 14:40:57 +0000514 run, with special ThreadId of one. This is called at startup; the
sewardje663cb92002-04-12 10:26:32 +0000515 caller takes care to park the client's state is parked in
516 VG_(baseBlock).
517*/
518void VG_(scheduler_init) ( void )
519{
520 Int i;
521 Addr startup_esp;
522 ThreadId tid_main;
523
524 startup_esp = VG_(baseBlock)[VGOFF_(m_esp)];
525 if ((startup_esp & VG_STARTUP_STACK_MASK) != VG_STARTUP_STACK_MASK) {
sewardj9a199dc2002-04-14 13:01:38 +0000526 VG_(printf)("%%esp at startup = %p is not near %p; aborting\n",
527 (void*)startup_esp, (void*)VG_STARTUP_STACK_MASK);
sewardje663cb92002-04-12 10:26:32 +0000528 VG_(panic)("unexpected %esp at startup");
529 }
530
sewardj6072c362002-04-19 14:40:57 +0000531 for (i = 0 /* NB; not 1 */; i < VG_N_THREADS; i++) {
532 vg_threads[i].status = VgTs_Empty;
sewardje663cb92002-04-12 10:26:32 +0000533 vg_threads[i].stack_size = 0;
534 vg_threads[i].stack_base = (Addr)NULL;
sewardj1e8cdc92002-04-18 11:37:52 +0000535 vg_threads[i].tid = i;
sewardje663cb92002-04-12 10:26:32 +0000536 }
537
538 for (i = 0; i < VG_N_WAITING_FDS; i++)
539 vg_waiting_fds[i].fd = -1; /* not in use */
540
sewardj5f07b662002-04-23 16:52:51 +0000541 for (i = 0; i < VG_N_THREAD_KEYS; i++) {
542 vg_thread_keys[i].inuse = False;
543 vg_thread_keys[i].destructor = NULL;
544 }
545
sewardje663cb92002-04-12 10:26:32 +0000546 /* Assert this is thread zero, which has certain magic
547 properties. */
548 tid_main = vg_alloc_ThreadState();
sewardj6072c362002-04-19 14:40:57 +0000549 vg_assert(tid_main == 1);
sewardje663cb92002-04-12 10:26:32 +0000550
sewardj3b5d8862002-04-20 13:53:23 +0000551 vg_threads[tid_main].status = VgTs_Runnable;
552 vg_threads[tid_main].joiner = VG_INVALID_THREADID;
553 vg_threads[tid_main].associated_mx = NULL;
554 vg_threads[tid_main].associated_cv = NULL;
555 vg_threads[tid_main].retval = NULL; /* not important */
sewardj1e8cdc92002-04-18 11:37:52 +0000556 vg_threads[tid_main].stack_highest_word
557 = vg_threads[tid_main].m_esp /* -4 ??? */;
sewardj5f07b662002-04-23 16:52:51 +0000558 for (i = 0; i < VG_N_THREAD_KEYS; i++)
559 vg_threads[tid_main].specifics[i] = NULL;
sewardje663cb92002-04-12 10:26:32 +0000560
561 /* Copy VG_(baseBlock) state to tid_main's slot. */
sewardj1e8cdc92002-04-18 11:37:52 +0000562 vg_tid_currently_in_baseBlock = tid_main;
sewardje663cb92002-04-12 10:26:32 +0000563 VG_(save_thread_state) ( tid_main );
sewardj1e8cdc92002-04-18 11:37:52 +0000564
565 /* So now ... */
566 vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
sewardje663cb92002-04-12 10:26:32 +0000567}
568
569
570/* What if fd isn't a valid fd? */
571static
572void set_fd_nonblocking ( Int fd )
573{
574 Int res = VG_(fcntl)( fd, VKI_F_GETFL, 0 );
575 vg_assert(!VG_(is_kerror)(res));
576 res |= VKI_O_NONBLOCK;
577 res = VG_(fcntl)( fd, VKI_F_SETFL, res );
578 vg_assert(!VG_(is_kerror)(res));
579}
580
581static
582void set_fd_blocking ( Int fd )
583{
584 Int res = VG_(fcntl)( fd, VKI_F_GETFL, 0 );
585 vg_assert(!VG_(is_kerror)(res));
586 res &= ~VKI_O_NONBLOCK;
587 res = VG_(fcntl)( fd, VKI_F_SETFL, res );
588 vg_assert(!VG_(is_kerror)(res));
589}
590
591static
592Bool fd_is_blockful ( Int fd )
593{
594 Int res = VG_(fcntl)( fd, VKI_F_GETFL, 0 );
595 vg_assert(!VG_(is_kerror)(res));
596 return (res & VKI_O_NONBLOCK) ? False : True;
597}
598
599
600
sewardjd7fd4d22002-04-24 01:57:27 +0000601/* Possibly do a for tid. Return values are:
sewardje663cb92002-04-12 10:26:32 +0000602
sewardjd7fd4d22002-04-24 01:57:27 +0000603 True = request done. Thread may or may not be still runnable;
604 caller must check. If it is still runnable, the result will be in
605 the thread's %EDX as expected.
606
607 False = request not done. A more capable but slower mechanism will
608 deal with it.
sewardje663cb92002-04-12 10:26:32 +0000609*/
sewardjd7fd4d22002-04-24 01:57:27 +0000610static
sewardje663cb92002-04-12 10:26:32 +0000611Bool maybe_do_trivial_clientreq ( ThreadId tid )
612{
613# define SIMPLE_RETURN(vvv) \
sewardj8c824512002-04-14 04:16:48 +0000614 { tst->m_edx = (vvv); \
sewardje663cb92002-04-12 10:26:32 +0000615 return True; \
616 }
617
sewardj8c824512002-04-14 04:16:48 +0000618 ThreadState* tst = &vg_threads[tid];
619 UInt* arg = (UInt*)(tst->m_eax);
620 UInt req_no = arg[0];
621
sewardje663cb92002-04-12 10:26:32 +0000622 switch (req_no) {
623 case VG_USERREQ__MALLOC:
624 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000625 (UInt)VG_(client_malloc) ( tst, arg[1], Vg_AllocMalloc )
sewardje663cb92002-04-12 10:26:32 +0000626 );
627 case VG_USERREQ__BUILTIN_NEW:
628 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000629 (UInt)VG_(client_malloc) ( tst, arg[1], Vg_AllocNew )
sewardje663cb92002-04-12 10:26:32 +0000630 );
631 case VG_USERREQ__BUILTIN_VEC_NEW:
632 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000633 (UInt)VG_(client_malloc) ( tst, arg[1], Vg_AllocNewVec )
sewardje663cb92002-04-12 10:26:32 +0000634 );
635 case VG_USERREQ__FREE:
sewardj8c824512002-04-14 04:16:48 +0000636 VG_(client_free) ( tst, (void*)arg[1], Vg_AllocMalloc );
sewardje663cb92002-04-12 10:26:32 +0000637 SIMPLE_RETURN(0); /* irrelevant */
638 case VG_USERREQ__BUILTIN_DELETE:
sewardj8c824512002-04-14 04:16:48 +0000639 VG_(client_free) ( tst, (void*)arg[1], Vg_AllocNew );
sewardje663cb92002-04-12 10:26:32 +0000640 SIMPLE_RETURN(0); /* irrelevant */
641 case VG_USERREQ__BUILTIN_VEC_DELETE:
sewardj8c824512002-04-14 04:16:48 +0000642 VG_(client_free) ( tst, (void*)arg[1], Vg_AllocNewVec );
sewardje663cb92002-04-12 10:26:32 +0000643 SIMPLE_RETURN(0); /* irrelevant */
644 case VG_USERREQ__CALLOC:
645 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000646 (UInt)VG_(client_calloc) ( tst, arg[1], arg[2] )
sewardje663cb92002-04-12 10:26:32 +0000647 );
648 case VG_USERREQ__REALLOC:
649 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000650 (UInt)VG_(client_realloc) ( tst, (void*)arg[1], arg[2] )
sewardje663cb92002-04-12 10:26:32 +0000651 );
652 case VG_USERREQ__MEMALIGN:
653 SIMPLE_RETURN(
sewardj8c824512002-04-14 04:16:48 +0000654 (UInt)VG_(client_memalign) ( tst, arg[1], arg[2] )
sewardje663cb92002-04-12 10:26:32 +0000655 );
sewardj9650c992002-04-16 03:44:31 +0000656
sewardj5f07b662002-04-23 16:52:51 +0000657 /* These are heavily used -- or at least we want them to be
658 cheap. */
sewardj9650c992002-04-16 03:44:31 +0000659 case VG_USERREQ__PTHREAD_GET_THREADID:
660 SIMPLE_RETURN(tid);
661 case VG_USERREQ__RUNNING_ON_VALGRIND:
662 SIMPLE_RETURN(1);
sewardj45b4b372002-04-16 22:50:32 +0000663 case VG_USERREQ__GET_PTHREAD_TRACE_LEVEL:
664 SIMPLE_RETURN(VG_(clo_trace_pthread_level));
sewardj5f07b662002-04-23 16:52:51 +0000665 case VG_USERREQ__READ_MILLISECOND_TIMER:
666 SIMPLE_RETURN(VG_(read_millisecond_timer)());
sewardj9650c992002-04-16 03:44:31 +0000667
sewardjd7fd4d22002-04-24 01:57:27 +0000668 case VG_USERREQ__PTHREAD_MUTEX_UNLOCK:
669 do_pthread_mutex_unlock( tid, (void *)(arg[1]) );
670 return True;
671
672 /* This may make thread tid non-runnable, but the scheduler
673 checks for that on return from this function. */
674 case VG_USERREQ__PTHREAD_MUTEX_LOCK:
675 do_pthread_mutex_lock( tid, False, (void *)(arg[1]) );
676 return True;
677
sewardj14e03422002-04-24 19:51:31 +0000678 case VG_USERREQ__PTHREAD_MUTEX_TRYLOCK:
679 do_pthread_mutex_lock( tid, True, (void *)(arg[1]) );
680 return True;
681
sewardj51c0aaf2002-04-25 01:32:10 +0000682 case VG_USERREQ__PTHREAD_GETSPECIFIC:
683 do_pthread_getspecific ( tid, (UInt)(arg[1]) );
684 return True;
685
sewardje663cb92002-04-12 10:26:32 +0000686 default:
687 /* Too hard; wimp out. */
688 return False;
689 }
690# undef SIMPLE_RETURN
691}
692
693
sewardj6072c362002-04-19 14:40:57 +0000694/* vthread tid is returning from a signal handler; modify its
695 stack/regs accordingly. */
696static
697void handle_signal_return ( ThreadId tid )
698{
699 Char msg_buf[100];
700 Bool restart_blocked_syscalls;
701
702 vg_assert(is_valid_tid(tid));
703
704 restart_blocked_syscalls = VG_(signal_returns)(tid);
705
706 if (restart_blocked_syscalls)
707 /* Easy; we don't have to do anything. */
708 return;
709
710 if (vg_threads[tid].status == VgTs_WaitFD) {
711 vg_assert(vg_threads[tid].m_eax == __NR_read
712 || vg_threads[tid].m_eax == __NR_write);
713 /* read() or write() interrupted. Force a return with EINTR. */
714 vg_threads[tid].m_eax = -VKI_EINTR;
715 vg_threads[tid].status = VgTs_Runnable;
716 if (VG_(clo_trace_sched)) {
717 VG_(sprintf)(msg_buf,
718 "read() / write() interrupted by signal; return EINTR" );
719 print_sched_event(tid, msg_buf);
720 }
721 return;
722 }
723
724 if (vg_threads[tid].status == VgTs_WaitFD) {
725 vg_assert(vg_threads[tid].m_eax == __NR_nanosleep);
726 /* We interrupted a nanosleep(). The right thing to do is to
727 write the unused time to nanosleep's second param and return
728 EINTR, but I'm too lazy for that. */
729 return;
730 }
731
732 /* All other cases? Just return. */
733}
734
735
sewardje663cb92002-04-12 10:26:32 +0000736static
737void sched_do_syscall ( ThreadId tid )
738{
739 UInt saved_eax;
740 UInt res, syscall_no;
741 UInt fd;
742 Bool might_block, assumed_nonblocking;
743 Bool orig_fd_blockness;
744 Char msg_buf[100];
745
sewardj6072c362002-04-19 14:40:57 +0000746 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +0000747 vg_assert(vg_threads[tid].status == VgTs_Runnable);
748
749 syscall_no = vg_threads[tid].m_eax; /* syscall number */
750
751 if (syscall_no == __NR_nanosleep) {
sewardj5f07b662002-04-23 16:52:51 +0000752 UInt t_now, t_awaken;
sewardje663cb92002-04-12 10:26:32 +0000753 struct vki_timespec* req;
754 req = (struct vki_timespec*)vg_threads[tid].m_ebx; /* arg1 */
sewardj5f07b662002-04-23 16:52:51 +0000755 t_now = VG_(read_millisecond_timer)();
sewardje663cb92002-04-12 10:26:32 +0000756 t_awaken
757 = t_now
sewardj5f07b662002-04-23 16:52:51 +0000758 + (UInt)1000ULL * (UInt)(req->tv_sec)
759 + (UInt)(req->tv_nsec) / 1000000;
sewardje663cb92002-04-12 10:26:32 +0000760 vg_threads[tid].status = VgTs_Sleeping;
761 vg_threads[tid].awaken_at = t_awaken;
sewardj8937c812002-04-12 20:12:20 +0000762 if (VG_(clo_trace_sched)) {
sewardj5f07b662002-04-23 16:52:51 +0000763 VG_(sprintf)(msg_buf, "at %d: nanosleep for %d",
sewardje663cb92002-04-12 10:26:32 +0000764 t_now, t_awaken-t_now);
765 print_sched_event(tid, msg_buf);
766 }
767 /* Force the scheduler to run something else for a while. */
768 return;
769 }
770
771 switch (syscall_no) {
772 case __NR_read:
773 case __NR_write:
774 assumed_nonblocking
775 = False;
776 might_block
777 = fd_is_blockful(vg_threads[tid].m_ebx /* arg1 */);
778 break;
779 default:
780 might_block = False;
781 assumed_nonblocking = True;
782 }
783
784 if (assumed_nonblocking) {
785 /* We think it's non-blocking. Just do it in the normal way. */
786 VG_(perform_assumed_nonblocking_syscall)(tid);
787 /* The thread is still runnable. */
788 return;
789 }
790
791 /* It might block. Take evasive action. */
792 switch (syscall_no) {
793 case __NR_read:
794 case __NR_write:
795 fd = vg_threads[tid].m_ebx; break;
796 default:
797 vg_assert(3+3 == 7);
798 }
799
800 /* Set the fd to nonblocking, and do the syscall, which will return
801 immediately, in order to lodge a request with the Linux kernel.
802 We later poll for I/O completion using select(). */
803
804 orig_fd_blockness = fd_is_blockful(fd);
805 set_fd_nonblocking(fd);
806 vg_assert(!fd_is_blockful(fd));
807 VG_(check_known_blocking_syscall)(tid, syscall_no, NULL /* PRE */);
808
809 /* This trashes the thread's %eax; we have to preserve it. */
810 saved_eax = vg_threads[tid].m_eax;
811 KERNEL_DO_SYSCALL(tid,res);
812
813 /* Restore original blockfulness of the fd. */
814 if (orig_fd_blockness)
815 set_fd_blocking(fd);
816 else
817 set_fd_nonblocking(fd);
818
819 if (res != -VKI_EWOULDBLOCK) {
820 /* It didn't block; it went through immediately. So finish off
821 in the normal way. Don't restore %EAX, since that now
822 (correctly) holds the result of the call. */
823 VG_(check_known_blocking_syscall)(tid, syscall_no, &res /* POST */);
824 /* We're still runnable. */
825 vg_assert(vg_threads[tid].status == VgTs_Runnable);
826
827 } else {
828
829 /* It would have blocked. First, restore %EAX to what it was
830 before our speculative call. */
831 vg_threads[tid].m_eax = saved_eax;
832 /* Put this fd in a table of fds on which we are waiting for
833 completion. The arguments for select() later are constructed
834 from this table. */
835 add_waiting_fd(tid, fd, saved_eax /* which holds the syscall # */);
836 /* Deschedule thread until an I/O completion happens. */
837 vg_threads[tid].status = VgTs_WaitFD;
sewardj8937c812002-04-12 20:12:20 +0000838 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +0000839 VG_(sprintf)(msg_buf,"block until I/O ready on fd %d", fd);
840 print_sched_event(tid, msg_buf);
841 }
842
843 }
844}
845
846
847/* Find out which of the fds in vg_waiting_fds are now ready to go, by
848 making enquiries with select(), and mark them as ready. We have to
849 wait for the requesting threads to fall into the the WaitFD state
850 before we can actually finally deliver the results, so this
851 procedure doesn't do that; complete_blocked_syscalls() does it.
852
853 It might seem odd that a thread which has done a blocking syscall
854 is not in WaitFD state; the way this can happen is if it initially
855 becomes WaitFD, but then a signal is delivered to it, so it becomes
856 Runnable for a while. In this case we have to wait for the
857 sighandler to return, whereupon the WaitFD state is resumed, and
858 only at that point can the I/O result be delivered to it. However,
859 this point may be long after the fd is actually ready.
860
861 So, poll_for_ready_fds() merely detects fds which are ready.
862 complete_blocked_syscalls() does the second half of the trick,
863 possibly much later: it delivers the results from ready fds to
864 threads in WaitFD state.
865*/
sewardj9a199dc2002-04-14 13:01:38 +0000866static
sewardje663cb92002-04-12 10:26:32 +0000867void poll_for_ready_fds ( void )
868{
869 vki_ksigset_t saved_procmask;
870 vki_fd_set readfds;
871 vki_fd_set writefds;
872 vki_fd_set exceptfds;
873 struct vki_timeval timeout;
874 Int fd, fd_max, i, n_ready, syscall_no, n_ok;
875 ThreadId tid;
876 Bool rd_ok, wr_ok, ex_ok;
877 Char msg_buf[100];
878
sewardje462e202002-04-13 04:09:07 +0000879 struct vki_timespec* rem;
sewardj5f07b662002-04-23 16:52:51 +0000880 UInt t_now;
sewardje462e202002-04-13 04:09:07 +0000881
sewardje663cb92002-04-12 10:26:32 +0000882 /* Awaken any sleeping threads whose sleep has expired. */
sewardj6072c362002-04-19 14:40:57 +0000883 for (tid = 1; tid < VG_N_THREADS; tid++)
884 if (vg_threads[tid].status == VgTs_Sleeping)
885 break;
886
sewardj5f07b662002-04-23 16:52:51 +0000887 /* Avoid pointless calls to VG_(read_millisecond_timer). */
sewardj6072c362002-04-19 14:40:57 +0000888 if (tid < VG_N_THREADS) {
sewardj5f07b662002-04-23 16:52:51 +0000889 t_now = VG_(read_millisecond_timer)();
sewardj6072c362002-04-19 14:40:57 +0000890 for (tid = 1; tid < VG_N_THREADS; tid++) {
891 if (vg_threads[tid].status != VgTs_Sleeping)
892 continue;
893 if (t_now >= vg_threads[tid].awaken_at) {
894 /* Resume this thread. Set to zero the remaining-time
895 (second) arg of nanosleep, since it's used up all its
896 time. */
897 vg_assert(vg_threads[tid].m_eax == __NR_nanosleep);
898 rem = (struct vki_timespec *)vg_threads[tid].m_ecx; /* arg2 */
899 if (rem != NULL) {
900 rem->tv_sec = 0;
901 rem->tv_nsec = 0;
902 }
903 /* Make the syscall return 0 (success). */
904 vg_threads[tid].m_eax = 0;
905 /* Reschedule this thread. */
906 vg_threads[tid].status = VgTs_Runnable;
907 if (VG_(clo_trace_sched)) {
sewardj5f07b662002-04-23 16:52:51 +0000908 VG_(sprintf)(msg_buf, "at %d: nanosleep done",
sewardj6072c362002-04-19 14:40:57 +0000909 t_now);
910 print_sched_event(tid, msg_buf);
911 }
sewardje663cb92002-04-12 10:26:32 +0000912 }
913 }
914 }
sewardje663cb92002-04-12 10:26:32 +0000915
sewardje462e202002-04-13 04:09:07 +0000916 /* And look for threads waiting on file descriptors which are now
917 ready for I/O.*/
sewardje663cb92002-04-12 10:26:32 +0000918 timeout.tv_sec = 0;
919 timeout.tv_usec = 0;
920
921 VKI_FD_ZERO(&readfds);
922 VKI_FD_ZERO(&writefds);
923 VKI_FD_ZERO(&exceptfds);
924 fd_max = -1;
925 for (i = 0; i < VG_N_WAITING_FDS; i++) {
926 if (vg_waiting_fds[i].fd == -1 /* not in use */)
927 continue;
928 if (vg_waiting_fds[i].ready /* already ready? */)
929 continue;
930 fd = vg_waiting_fds[i].fd;
931 /* VG_(printf)("adding QUERY for fd %d\n", fd); */
sewardje462e202002-04-13 04:09:07 +0000932 vg_assert(fd >= 0);
sewardje663cb92002-04-12 10:26:32 +0000933 if (fd > fd_max)
934 fd_max = fd;
935 tid = vg_waiting_fds[i].tid;
sewardj6072c362002-04-19 14:40:57 +0000936 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +0000937 syscall_no = vg_waiting_fds[i].syscall_no;
938 switch (syscall_no) {
939 case __NR_read:
940 VKI_FD_SET(fd, &readfds); break;
941 case __NR_write:
942 VKI_FD_SET(fd, &writefds); break;
943 default:
944 VG_(panic)("poll_for_ready_fds: unexpected syscall");
945 /*NOTREACHED*/
946 break;
947 }
948 }
949
sewardje462e202002-04-13 04:09:07 +0000950 /* Short cut: if no fds are waiting, give up now. */
951 if (fd_max == -1)
952 return;
953
sewardje663cb92002-04-12 10:26:32 +0000954 /* BLOCK ALL SIGNALS. We don't want the complication of select()
955 getting interrupted. */
956 VG_(block_all_host_signals)( &saved_procmask );
957
958 n_ready = VG_(select)
959 ( fd_max+1, &readfds, &writefds, &exceptfds, &timeout);
960 if (VG_(is_kerror)(n_ready)) {
961 VG_(printf)("poll_for_ready_fds: select returned %d\n", n_ready);
962 VG_(panic)("poll_for_ready_fds: select failed?!");
963 /*NOTREACHED*/
964 }
965
966 /* UNBLOCK ALL SIGNALS */
967 VG_(restore_host_signals)( &saved_procmask );
968
969 /* VG_(printf)("poll_for_io_completions: %d fs ready\n", n_ready); */
970
971 if (n_ready == 0)
972 return;
973
974 /* Inspect all the fds we know about, and handle any completions that
975 have happened. */
976 /*
977 VG_(printf)("\n\n");
978 for (fd = 0; fd < 100; fd++)
979 if (VKI_FD_ISSET(fd, &writefds) || VKI_FD_ISSET(fd, &readfds)) {
980 VG_(printf)("X"); } else { VG_(printf)("."); };
981 VG_(printf)("\n\nfd_max = %d\n", fd_max);
982 */
983
984 for (fd = 0; fd <= fd_max; fd++) {
985 rd_ok = VKI_FD_ISSET(fd, &readfds);
986 wr_ok = VKI_FD_ISSET(fd, &writefds);
987 ex_ok = VKI_FD_ISSET(fd, &exceptfds);
988
989 n_ok = (rd_ok ? 1 : 0) + (wr_ok ? 1 : 0) + (ex_ok ? 1 : 0);
990 if (n_ok == 0)
991 continue;
992 if (n_ok > 1) {
993 VG_(printf)("offending fd = %d\n", fd);
994 VG_(panic)("poll_for_ready_fds: multiple events on fd");
995 }
996
997 /* An I/O event completed for fd. Find the thread which
998 requested this. */
999 for (i = 0; i < VG_N_WAITING_FDS; i++) {
1000 if (vg_waiting_fds[i].fd == -1 /* not in use */)
1001 continue;
1002 if (vg_waiting_fds[i].fd == fd)
1003 break;
1004 }
1005
1006 /* And a bit more paranoia ... */
1007 vg_assert(i >= 0 && i < VG_N_WAITING_FDS);
1008
1009 /* Mark the fd as ready. */
1010 vg_assert(! vg_waiting_fds[i].ready);
1011 vg_waiting_fds[i].ready = True;
1012 }
1013}
1014
1015
1016/* See comment attached to poll_for_ready_fds() for explaination. */
sewardj9a199dc2002-04-14 13:01:38 +00001017static
sewardje663cb92002-04-12 10:26:32 +00001018void complete_blocked_syscalls ( void )
1019{
1020 Int fd, i, res, syscall_no;
1021 ThreadId tid;
1022 Char msg_buf[100];
1023
1024 /* Inspect all the outstanding fds we know about. */
1025
1026 for (i = 0; i < VG_N_WAITING_FDS; i++) {
1027 if (vg_waiting_fds[i].fd == -1 /* not in use */)
1028 continue;
1029 if (! vg_waiting_fds[i].ready)
1030 continue;
1031
1032 fd = vg_waiting_fds[i].fd;
1033 tid = vg_waiting_fds[i].tid;
sewardj6072c362002-04-19 14:40:57 +00001034 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001035
1036 /* The thread actually has to be waiting for the I/O event it
1037 requested before we can deliver the result! */
1038 if (vg_threads[tid].status != VgTs_WaitFD)
1039 continue;
1040
1041 /* Ok, actually do it! We can safely use %EAX as the syscall
1042 number, because the speculative call made by
1043 sched_do_syscall() doesn't change %EAX in the case where the
1044 call would have blocked. */
1045
1046 syscall_no = vg_waiting_fds[i].syscall_no;
1047 vg_assert(syscall_no == vg_threads[tid].m_eax);
1048 KERNEL_DO_SYSCALL(tid,res);
1049 VG_(check_known_blocking_syscall)(tid, syscall_no, &res /* POST */);
1050
1051 /* Reschedule. */
1052 vg_threads[tid].status = VgTs_Runnable;
1053 /* Mark slot as no longer in use. */
1054 vg_waiting_fds[i].fd = -1;
1055 /* pp_sched_status(); */
sewardj8937c812002-04-12 20:12:20 +00001056 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001057 VG_(sprintf)(msg_buf,"resume due to I/O completion on fd %d", fd);
1058 print_sched_event(tid, msg_buf);
1059 }
1060 }
1061}
1062
1063
1064static
sewardj5f07b662002-04-23 16:52:51 +00001065void check_for_pthread_cond_timedwait ( void )
1066{
sewardj51c0aaf2002-04-25 01:32:10 +00001067 Int i, now;
sewardj5f07b662002-04-23 16:52:51 +00001068 for (i = 1; i < VG_N_THREADS; i++) {
1069 if (vg_threads[i].status != VgTs_WaitCV)
1070 continue;
1071 if (vg_threads[i].awaken_at == 0xFFFFFFFF /* no timeout */)
1072 continue;
sewardj51c0aaf2002-04-25 01:32:10 +00001073 now = VG_(read_millisecond_timer)();
1074 if (now >= vg_threads[i].awaken_at) {
sewardj5f07b662002-04-23 16:52:51 +00001075 do_pthread_cond_timedwait_TIMEOUT(i);
sewardj51c0aaf2002-04-25 01:32:10 +00001076 }
sewardj5f07b662002-04-23 16:52:51 +00001077 }
1078}
1079
1080
1081static
sewardje663cb92002-04-12 10:26:32 +00001082void nanosleep_for_a_while ( void )
1083{
1084 Int res;
1085 struct vki_timespec req;
1086 struct vki_timespec rem;
1087 req.tv_sec = 0;
sewardj51c0aaf2002-04-25 01:32:10 +00001088 req.tv_nsec = 20 * 1000 * 1000;
sewardje663cb92002-04-12 10:26:32 +00001089 res = VG_(nanosleep)( &req, &rem );
sewardj5f07b662002-04-23 16:52:51 +00001090 vg_assert(res == 0 /* ok */ || res == 1 /* interrupted by signal */);
sewardje663cb92002-04-12 10:26:32 +00001091}
1092
1093
1094/* ---------------------------------------------------------------------
1095 The scheduler proper.
1096 ------------------------------------------------------------------ */
1097
1098/* Run user-space threads until either
1099 * Deadlock occurs
1100 * One thread asks to shutdown Valgrind
1101 * The specified number of basic blocks has gone by.
1102*/
1103VgSchedReturnCode VG_(scheduler) ( void )
1104{
1105 ThreadId tid, tid_next;
1106 UInt trc;
1107 UInt dispatch_ctr_SAVED;
sewardj51c0aaf2002-04-25 01:32:10 +00001108 Int request_code, done_this_time, n_in_bounded_wait;
sewardje663cb92002-04-12 10:26:32 +00001109 Char msg_buf[100];
1110 Addr trans_addr;
sewardj14e03422002-04-24 19:51:31 +00001111 Bool sigs_delivered;
sewardje663cb92002-04-12 10:26:32 +00001112
1113 /* For the LRU structures, records when the epoch began. */
1114 ULong lru_epoch_started_at = 0;
1115
1116 /* Start with the root thread. tid in general indicates the
1117 currently runnable/just-finished-running thread. */
sewardj6072c362002-04-19 14:40:57 +00001118 tid = 1;
sewardje663cb92002-04-12 10:26:32 +00001119
1120 /* This is the top level scheduler loop. It falls into three
1121 phases. */
1122 while (True) {
1123
sewardj6072c362002-04-19 14:40:57 +00001124 /* ======================= Phase 0 of 3 =======================
1125 Be paranoid. Always a good idea. */
sewardjd7fd4d22002-04-24 01:57:27 +00001126 stage1:
sewardj6072c362002-04-19 14:40:57 +00001127 scheduler_sanity();
1128
sewardje663cb92002-04-12 10:26:32 +00001129 /* ======================= Phase 1 of 3 =======================
1130 Handle I/O completions and signals. This may change the
1131 status of various threads. Then select a new thread to run,
1132 or declare deadlock, or sleep if there are no runnable
1133 threads but some are blocked on I/O. */
1134
1135 /* Age the LRU structures if an epoch has been completed. */
1136 if (VG_(bbs_done) - lru_epoch_started_at >= VG_BBS_PER_EPOCH) {
1137 lru_epoch_started_at = VG_(bbs_done);
1138 increment_epoch();
1139 }
1140
1141 /* Was a debug-stop requested? */
1142 if (VG_(bbs_to_go) == 0)
1143 goto debug_stop;
1144
1145 /* Do the following loop until a runnable thread is found, or
1146 deadlock is detected. */
1147 while (True) {
1148
1149 /* For stats purposes only. */
1150 VG_(num_scheduling_events_MAJOR) ++;
1151
1152 /* See if any I/O operations which we were waiting for have
1153 completed, and, if so, make runnable the relevant waiting
1154 threads. */
1155 poll_for_ready_fds();
1156 complete_blocked_syscalls();
sewardj5f07b662002-04-23 16:52:51 +00001157 check_for_pthread_cond_timedwait();
sewardje663cb92002-04-12 10:26:32 +00001158
1159 /* See if there are any signals which need to be delivered. If
1160 so, choose thread(s) to deliver them to, and build signal
1161 delivery frames on those thread(s) stacks. */
sewardj6072c362002-04-19 14:40:57 +00001162
1163 /* Be careful about delivering signals to a thread waiting
1164 for a mutex. In particular, when the handler is running,
1165 that thread is temporarily apparently-not-waiting for the
1166 mutex, so if it is unlocked by another thread whilst the
1167 handler is running, this thread is not informed. When the
1168 handler returns, the thread resumes waiting on the mutex,
1169 even if, as a result, it has missed the unlocking of it.
1170 Potential deadlock. This sounds all very strange, but the
1171 POSIX standard appears to require this behaviour. */
sewardj14e03422002-04-24 19:51:31 +00001172 sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
1173 if (sigs_delivered)
1174 VG_(do_sanity_checks)( 1 /*HACK*/, False );
sewardje663cb92002-04-12 10:26:32 +00001175
1176 /* Try and find a thread (tid) to run. */
1177 tid_next = tid;
sewardj51c0aaf2002-04-25 01:32:10 +00001178 n_in_bounded_wait = 0;
sewardje663cb92002-04-12 10:26:32 +00001179 while (True) {
1180 tid_next++;
sewardj6072c362002-04-19 14:40:57 +00001181 if (tid_next >= VG_N_THREADS) tid_next = 1;
sewardj54cacf02002-04-12 23:24:59 +00001182 if (vg_threads[tid_next].status == VgTs_WaitFD
sewardj51c0aaf2002-04-25 01:32:10 +00001183 || vg_threads[tid_next].status == VgTs_Sleeping
1184 || (vg_threads[tid_next].status == VgTs_WaitCV
1185 && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
1186 n_in_bounded_wait ++;
sewardje663cb92002-04-12 10:26:32 +00001187 if (vg_threads[tid_next].status == VgTs_Runnable)
1188 break; /* We can run this one. */
1189 if (tid_next == tid)
1190 break; /* been all the way round */
1191 }
1192 tid = tid_next;
1193
1194 if (vg_threads[tid].status == VgTs_Runnable) {
1195 /* Found a suitable candidate. Fall out of this loop, so
1196 we can advance to stage 2 of the scheduler: actually
1197 running the thread. */
1198 break;
1199 }
1200
1201 /* We didn't find a runnable thread. Now what? */
sewardj51c0aaf2002-04-25 01:32:10 +00001202 if (n_in_bounded_wait == 0) {
sewardj54cacf02002-04-12 23:24:59 +00001203 /* No runnable threads and no prospect of any appearing
1204 even if we wait for an arbitrary length of time. In
1205 short, we have a deadlock. */
sewardj15a43e12002-04-17 19:35:12 +00001206 VG_(pp_sched_status)();
sewardje663cb92002-04-12 10:26:32 +00001207 return VgSrc_Deadlock;
1208 }
1209
1210 /* At least one thread is in a fd-wait state. Delay for a
1211 while, and go round again, in the hope that eventually a
1212 thread becomes runnable. */
1213 nanosleep_for_a_while();
1214 // pp_sched_status();
1215 // VG_(printf)(".\n");
1216 }
1217
1218
1219 /* ======================= Phase 2 of 3 =======================
1220 Wahey! We've finally decided that thread tid is runnable, so
1221 we now do that. Run it for as much of a quanta as possible.
1222 Trivial requests are handled and the thread continues. The
1223 aim is not to do too many of Phase 1 since it is expensive. */
1224
1225 if (0)
sewardj3b5d8862002-04-20 13:53:23 +00001226 VG_(printf)("SCHED: tid %d\n", tid);
sewardje663cb92002-04-12 10:26:32 +00001227
1228 /* Figure out how many bbs to ask vg_run_innerloop to do. Note
1229 that it decrements the counter before testing it for zero, so
1230 that if VG_(dispatch_ctr) is set to N you get at most N-1
1231 iterations. Also this means that VG_(dispatch_ctr) must
1232 exceed zero before entering the innerloop. Also also, the
1233 decrement is done before the bb is actually run, so you
1234 always get at least one decrement even if nothing happens.
1235 */
1236 if (VG_(bbs_to_go) >= VG_SCHEDULING_QUANTUM)
1237 VG_(dispatch_ctr) = VG_SCHEDULING_QUANTUM + 1;
1238 else
1239 VG_(dispatch_ctr) = (UInt)VG_(bbs_to_go) + 1;
1240
1241 /* ... and remember what we asked for. */
1242 dispatch_ctr_SAVED = VG_(dispatch_ctr);
1243
sewardj1e8cdc92002-04-18 11:37:52 +00001244 /* paranoia ... */
1245 vg_assert(vg_threads[tid].tid == tid);
1246
sewardje663cb92002-04-12 10:26:32 +00001247 /* Actually run thread tid. */
1248 while (True) {
1249
1250 /* For stats purposes only. */
1251 VG_(num_scheduling_events_MINOR) ++;
1252
1253 if (0)
1254 VG_(message)(Vg_DebugMsg, "thread %d: running for %d bbs",
1255 tid, VG_(dispatch_ctr) - 1 );
1256
1257 trc = run_thread_for_a_while ( tid );
1258
1259 /* Deal quickly with trivial scheduling events, and resume the
1260 thread. */
1261
1262 if (trc == VG_TRC_INNER_FASTMISS) {
1263 vg_assert(VG_(dispatch_ctr) > 0);
1264
1265 /* Trivial event. Miss in the fast-cache. Do a full
1266 lookup for it. */
1267 trans_addr
1268 = VG_(search_transtab) ( vg_threads[tid].m_eip );
1269 if (trans_addr == (Addr)0) {
1270 /* Not found; we need to request a translation. */
sewardj1e8cdc92002-04-18 11:37:52 +00001271 create_translation_for( tid, vg_threads[tid].m_eip );
sewardje663cb92002-04-12 10:26:32 +00001272 trans_addr = VG_(search_transtab) ( vg_threads[tid].m_eip );
1273 if (trans_addr == (Addr)0)
1274 VG_(panic)("VG_TRC_INNER_FASTMISS: missing tt_fast entry");
1275 }
1276 continue; /* with this thread */
1277 }
1278
1279 if (trc == VG_TRC_EBP_JMP_CLIENTREQ) {
sewardjd7fd4d22002-04-24 01:57:27 +00001280 Bool done = maybe_do_trivial_clientreq(tid);
1281 if (done) {
1282 /* The request is done. We try and continue with the
1283 same thread if still runnable. If not, go back to
1284 Stage 1 to select a new thread to run. */
1285 if (vg_threads[tid].status == VgTs_Runnable)
1286 continue; /* with this thread */
1287 else
1288 goto stage1;
sewardje663cb92002-04-12 10:26:32 +00001289 }
1290 }
1291
sewardj51c0aaf2002-04-25 01:32:10 +00001292 if (trc == VG_TRC_EBP_JMP_SYSCALL) {
1293 /* Do a syscall for the vthread tid. This could cause it
1294 to become non-runnable. */
1295 sched_do_syscall(tid);
1296 if (vg_threads[tid].status == VgTs_Runnable)
1297 continue; /* with this thread */
1298 else
1299 goto stage1;
1300 }
1301
sewardjd7fd4d22002-04-24 01:57:27 +00001302 /* It's an event we can't quickly deal with. Give up running
1303 this thread and handle things the expensive way. */
sewardje663cb92002-04-12 10:26:32 +00001304 break;
1305 }
1306
1307 /* ======================= Phase 3 of 3 =======================
1308 Handle non-trivial thread requests, mostly pthread stuff. */
1309
1310 /* Ok, we've fallen out of the dispatcher for a
1311 non-completely-trivial reason. First, update basic-block
1312 counters. */
1313
1314 done_this_time = (Int)dispatch_ctr_SAVED - (Int)VG_(dispatch_ctr) - 1;
1315 vg_assert(done_this_time >= 0);
1316 VG_(bbs_to_go) -= (ULong)done_this_time;
1317 VG_(bbs_done) += (ULong)done_this_time;
1318
1319 if (0 && trc != VG_TRC_INNER_FASTMISS)
1320 VG_(message)(Vg_DebugMsg, "thread %d: completed %d bbs, trc %d",
1321 tid, done_this_time, (Int)trc );
1322
1323 if (0 && trc != VG_TRC_INNER_FASTMISS)
1324 VG_(message)(Vg_DebugMsg, "thread %d: %ld bbs, event %s",
1325 tid, VG_(bbs_done),
1326 name_of_sched_event(trc) );
sewardj9d1b5d32002-04-17 19:40:49 +00001327
sewardje663cb92002-04-12 10:26:32 +00001328 /* Examine the thread's return code to figure out why it
1329 stopped, and handle requests. */
1330
1331 switch (trc) {
1332
1333 case VG_TRC_INNER_FASTMISS:
1334 VG_(panic)("VG_(scheduler): VG_TRC_INNER_FASTMISS");
1335 /*NOTREACHED*/
1336 break;
1337
1338 case VG_TRC_INNER_COUNTERZERO:
1339 /* Timeslice is out. Let a new thread be scheduled,
1340 simply by doing nothing, causing us to arrive back at
1341 Phase 1. */
1342 if (VG_(bbs_to_go) == 0) {
1343 goto debug_stop;
1344 }
1345 vg_assert(VG_(dispatch_ctr) == 0);
1346 break;
1347
1348 case VG_TRC_UNRESUMABLE_SIGNAL:
1349 /* It got a SIGSEGV/SIGBUS, which we need to deliver right
1350 away. Again, do nothing, so we wind up back at Phase
1351 1, whereupon the signal will be "delivered". */
1352 break;
1353
sewardj51c0aaf2002-04-25 01:32:10 +00001354#if 0
sewardje663cb92002-04-12 10:26:32 +00001355 case VG_TRC_EBP_JMP_SYSCALL:
1356 /* Do a syscall for the vthread tid. This could cause it
1357 to become non-runnable. */
1358 sched_do_syscall(tid);
1359 break;
sewardj51c0aaf2002-04-25 01:32:10 +00001360#endif
sewardje663cb92002-04-12 10:26:32 +00001361
1362 case VG_TRC_EBP_JMP_CLIENTREQ:
1363 /* Do a client request for the vthread tid. Note that
1364 some requests will have been handled by
1365 maybe_do_trivial_clientreq(), so we don't expect to see
1366 those here.
1367 */
sewardj54cacf02002-04-12 23:24:59 +00001368 /* The thread's %EAX points at an arg block, the first
1369 word of which is the request code. */
1370 request_code = ((UInt*)(vg_threads[tid].m_eax))[0];
sewardje663cb92002-04-12 10:26:32 +00001371 if (0) {
sewardj54cacf02002-04-12 23:24:59 +00001372 VG_(sprintf)(msg_buf, "request 0x%x", request_code );
sewardje663cb92002-04-12 10:26:32 +00001373 print_sched_event(tid, msg_buf);
1374 }
1375 /* Do a non-trivial client request for thread tid. tid's
1376 %EAX points to a short vector of argument words, the
1377 first of which is the request code. The result of the
1378 request is put in tid's %EDX. Alternatively, perhaps
1379 the request causes tid to become non-runnable and/or
1380 other blocked threads become runnable. In general we
1381 can and often do mess with the state of arbitrary
1382 threads at this point. */
sewardj54cacf02002-04-12 23:24:59 +00001383 if (request_code == VG_USERREQ__SHUTDOWN_VALGRIND) {
1384 return VgSrc_Shutdown;
1385 } else {
1386 do_nontrivial_clientreq(tid);
1387 }
sewardje663cb92002-04-12 10:26:32 +00001388 break;
1389
1390 default:
1391 VG_(printf)("\ntrc = %d\n", trc);
1392 VG_(panic)("VG_(scheduler), phase 3: "
1393 "unexpected thread return code");
1394 /* NOTREACHED */
1395 break;
1396
1397 } /* switch (trc) */
1398
1399 /* That completes Phase 3 of 3. Return now to the top of the
1400 main scheduler loop, to Phase 1 of 3. */
1401
1402 } /* top-level scheduler loop */
1403
1404
1405 /* NOTREACHED */
1406 VG_(panic)("scheduler: post-main-loop ?!");
1407 /* NOTREACHED */
1408
1409 debug_stop:
1410 /* If we exited because of a debug stop, print the translation
1411 of the last block executed -- by translating it again, and
1412 throwing away the result. */
1413 VG_(printf)(
1414 "======vvvvvvvv====== LAST TRANSLATION ======vvvvvvvv======\n");
sewardj1e8cdc92002-04-18 11:37:52 +00001415 VG_(translate)( &vg_threads[tid], vg_threads[tid].m_eip, NULL, NULL, NULL );
sewardje663cb92002-04-12 10:26:32 +00001416 VG_(printf)("\n");
1417 VG_(printf)(
1418 "======^^^^^^^^====== LAST TRANSLATION ======^^^^^^^^======\n");
1419
1420 return VgSrc_BbsDone;
1421}
1422
1423
1424/* ---------------------------------------------------------------------
1425 The pthread implementation.
1426 ------------------------------------------------------------------ */
1427
1428#include <pthread.h>
1429#include <errno.h>
1430
1431#if !defined(PTHREAD_STACK_MIN)
1432# define PTHREAD_STACK_MIN (16384 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB)
1433#endif
1434
1435/* /usr/include/bits/pthreadtypes.h:
1436 typedef unsigned long int pthread_t;
1437*/
1438
sewardje663cb92002-04-12 10:26:32 +00001439
sewardj604ec3c2002-04-18 22:38:41 +00001440/* -----------------------------------------------------------
1441 Thread CREATION, JOINAGE and CANCELLATION.
1442 -------------------------------------------------------- */
1443
sewardje663cb92002-04-12 10:26:32 +00001444static
1445void do_pthread_cancel ( ThreadId tid_canceller,
1446 pthread_t tid_cancellee )
1447{
1448 Char msg_buf[100];
1449 /* We want make is appear that this thread has returned to
1450 do_pthread_create_bogusRA with PTHREAD_CANCELED as the
1451 return value. So: simple: put PTHREAD_CANCELED into %EAX
1452 and &do_pthread_create_bogusRA into %EIP and keep going! */
sewardj8937c812002-04-12 20:12:20 +00001453 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001454 VG_(sprintf)(msg_buf, "cancelled by %d", tid_canceller);
1455 print_sched_event(tid_cancellee, msg_buf);
1456 }
1457 vg_threads[tid_cancellee].m_eax = (UInt)PTHREAD_CANCELED;
sewardjbc5b99f2002-04-13 00:08:51 +00001458 vg_threads[tid_cancellee].m_eip = (UInt)&VG_(pthreadreturn_bogusRA);
sewardje663cb92002-04-12 10:26:32 +00001459 vg_threads[tid_cancellee].status = VgTs_Runnable;
1460}
1461
1462
sewardj3b5d8862002-04-20 13:53:23 +00001463static
1464void do_pthread_exit ( ThreadId tid, void* retval )
1465{
1466 Char msg_buf[100];
1467 /* We want make is appear that this thread has returned to
1468 do_pthread_create_bogusRA with retval as the
1469 return value. So: simple: put retval into %EAX
1470 and &do_pthread_create_bogusRA into %EIP and keep going! */
1471 if (VG_(clo_trace_sched)) {
1472 VG_(sprintf)(msg_buf, "exiting with %p", retval);
1473 print_sched_event(tid, msg_buf);
1474 }
1475 vg_threads[tid].m_eax = (UInt)retval;
1476 vg_threads[tid].m_eip = (UInt)&VG_(pthreadreturn_bogusRA);
1477 vg_threads[tid].status = VgTs_Runnable;
1478}
1479
sewardje663cb92002-04-12 10:26:32 +00001480
1481/* Thread tid is exiting, by returning from the function it was
sewardjbc5b99f2002-04-13 00:08:51 +00001482 created with. Or possibly due to pthread_exit or cancellation.
1483 The main complication here is to resume any thread waiting to join
1484 with this one. */
sewardje663cb92002-04-12 10:26:32 +00001485static
sewardjbc5b99f2002-04-13 00:08:51 +00001486void handle_pthread_return ( ThreadId tid, void* retval )
sewardje663cb92002-04-12 10:26:32 +00001487{
1488 ThreadId jnr; /* joiner, the thread calling pthread_join. */
1489 UInt* jnr_args;
1490 void** jnr_thread_return;
1491 Char msg_buf[100];
1492
1493 /* Mark it as not in use. Leave the stack in place so the next
1494 user of this slot doesn't reallocate it. */
sewardj6072c362002-04-19 14:40:57 +00001495 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001496 vg_assert(vg_threads[tid].status != VgTs_Empty);
1497
sewardjbc5b99f2002-04-13 00:08:51 +00001498 vg_threads[tid].retval = retval;
sewardje663cb92002-04-12 10:26:32 +00001499
1500 if (vg_threads[tid].joiner == VG_INVALID_THREADID) {
1501 /* No one has yet done a join on me */
1502 vg_threads[tid].status = VgTs_WaitJoiner;
sewardj8937c812002-04-12 20:12:20 +00001503 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001504 VG_(sprintf)(msg_buf,
1505 "root fn returns, waiting for a call pthread_join(%d)",
1506 tid);
1507 print_sched_event(tid, msg_buf);
1508 }
1509 } else {
1510 /* Some is waiting; make their join call return with success,
1511 putting my exit code in the place specified by the caller's
1512 thread_return param. This is all very horrible, since we
1513 need to consult the joiner's arg block -- pointed to by its
1514 %EAX -- in order to extract the 2nd param of its pthread_join
1515 call. TODO: free properly the slot (also below).
1516 */
1517 jnr = vg_threads[tid].joiner;
sewardj6072c362002-04-19 14:40:57 +00001518 vg_assert(is_valid_tid(jnr));
sewardje663cb92002-04-12 10:26:32 +00001519 vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
1520 jnr_args = (UInt*)vg_threads[jnr].m_eax;
1521 jnr_thread_return = (void**)(jnr_args[2]);
1522 if (jnr_thread_return != NULL)
1523 *jnr_thread_return = vg_threads[tid].retval;
1524 vg_threads[jnr].m_edx = 0; /* success */
1525 vg_threads[jnr].status = VgTs_Runnable;
1526 vg_threads[tid].status = VgTs_Empty; /* bye! */
sewardj75fe1892002-04-14 02:46:33 +00001527 if (VG_(clo_instrument) && tid != 0)
1528 VGM_(make_noaccess)( vg_threads[tid].stack_base,
1529 vg_threads[tid].stack_size );
sewardj8937c812002-04-12 20:12:20 +00001530 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001531 VG_(sprintf)(msg_buf,
1532 "root fn returns, to find a waiting pthread_join(%d)", tid);
1533 print_sched_event(tid, msg_buf);
1534 VG_(sprintf)(msg_buf,
1535 "my pthread_join(%d) returned; resuming", tid);
1536 print_sched_event(jnr, msg_buf);
1537 }
1538 }
1539
1540 /* Return value is irrelevant; this thread will not get
1541 rescheduled. */
1542}
1543
1544
1545static
1546void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
1547{
1548 Char msg_buf[100];
1549
1550 /* jee, the joinee, is the thread specified as an arg in thread
1551 tid's call to pthread_join. So tid is the join-er. */
sewardj6072c362002-04-19 14:40:57 +00001552 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001553 vg_assert(vg_threads[tid].status == VgTs_Runnable);
1554
1555 if (jee == tid) {
1556 vg_threads[tid].m_edx = EDEADLK; /* libc constant, not a kernel one */
1557 vg_threads[tid].status = VgTs_Runnable;
1558 return;
1559 }
1560
1561 if (jee < 0
1562 || jee >= VG_N_THREADS
1563 || vg_threads[jee].status == VgTs_Empty) {
1564 /* Invalid thread to join to. */
1565 vg_threads[tid].m_edx = EINVAL;
1566 vg_threads[tid].status = VgTs_Runnable;
1567 return;
1568 }
1569
1570 if (vg_threads[jee].joiner != VG_INVALID_THREADID) {
1571 /* Someone already did join on this thread */
1572 vg_threads[tid].m_edx = EINVAL;
1573 vg_threads[tid].status = VgTs_Runnable;
1574 return;
1575 }
1576
1577 /* if (vg_threads[jee].detached) ... */
1578
1579 /* Perhaps the joinee has already finished? If so return
1580 immediately with its return code, and free up the slot. TODO:
1581 free it properly (also above). */
1582 if (vg_threads[jee].status == VgTs_WaitJoiner) {
1583 vg_assert(vg_threads[jee].joiner == VG_INVALID_THREADID);
1584 vg_threads[tid].m_edx = 0; /* success */
1585 if (thread_return != NULL)
1586 *thread_return = vg_threads[jee].retval;
1587 vg_threads[tid].status = VgTs_Runnable;
1588 vg_threads[jee].status = VgTs_Empty; /* bye! */
sewardj75fe1892002-04-14 02:46:33 +00001589 if (VG_(clo_instrument) && jee != 0)
1590 VGM_(make_noaccess)( vg_threads[jee].stack_base,
1591 vg_threads[jee].stack_size );
sewardj8937c812002-04-12 20:12:20 +00001592 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001593 VG_(sprintf)(msg_buf,
1594 "someone called pthread_join() on me; bye!");
1595 print_sched_event(jee, msg_buf);
1596 VG_(sprintf)(msg_buf,
1597 "my pthread_join(%d) returned immediately",
1598 jee );
1599 print_sched_event(tid, msg_buf);
1600 }
1601 return;
1602 }
1603
1604 /* Ok, so we'll have to wait on jee. */
1605 vg_threads[jee].joiner = tid;
1606 vg_threads[tid].status = VgTs_WaitJoinee;
sewardj8937c812002-04-12 20:12:20 +00001607 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001608 VG_(sprintf)(msg_buf,
1609 "blocking on call of pthread_join(%d)", jee );
1610 print_sched_event(tid, msg_buf);
1611 }
1612 /* So tid's join call does not return just now. */
1613}
1614
1615
1616static
1617void do_pthread_create ( ThreadId parent_tid,
1618 pthread_t* thread,
1619 pthread_attr_t* attr,
1620 void* (*start_routine)(void *),
1621 void* arg )
1622{
sewardj5f07b662002-04-23 16:52:51 +00001623 Int i;
sewardje663cb92002-04-12 10:26:32 +00001624 Addr new_stack;
1625 UInt new_stk_szb;
1626 ThreadId tid;
1627 Char msg_buf[100];
1628
1629 /* Paranoia ... */
1630 vg_assert(sizeof(pthread_t) == sizeof(UInt));
1631
1632 vg_assert(vg_threads[parent_tid].status != VgTs_Empty);
1633
sewardj1e8cdc92002-04-18 11:37:52 +00001634 tid = vg_alloc_ThreadState();
sewardje663cb92002-04-12 10:26:32 +00001635
1636 /* If we've created the main thread's tid, we're in deep trouble :) */
sewardj6072c362002-04-19 14:40:57 +00001637 vg_assert(tid != 1);
1638 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001639
1640 /* Copy the parent's CPU state into the child's, in a roundabout
1641 way (via baseBlock). */
1642 VG_(load_thread_state)(parent_tid);
1643 VG_(save_thread_state)(tid);
1644
1645 /* Consider allocating the child a stack, if the one it already has
1646 is inadequate. */
1647 new_stk_szb = PTHREAD_STACK_MIN;
1648
1649 if (new_stk_szb > vg_threads[tid].stack_size) {
1650 /* Again, for good measure :) We definitely don't want to be
1651 allocating a stack for the main thread. */
sewardj6072c362002-04-19 14:40:57 +00001652 vg_assert(tid != 1);
sewardje663cb92002-04-12 10:26:32 +00001653 /* for now, we don't handle the case of anything other than
1654 assigning it for the first time. */
1655 vg_assert(vg_threads[tid].stack_size == 0);
1656 vg_assert(vg_threads[tid].stack_base == (Addr)NULL);
1657 new_stack = (Addr)VG_(get_memory_from_mmap)( new_stk_szb );
1658 vg_threads[tid].stack_base = new_stack;
1659 vg_threads[tid].stack_size = new_stk_szb;
sewardj1e8cdc92002-04-18 11:37:52 +00001660 vg_threads[tid].stack_highest_word
sewardje663cb92002-04-12 10:26:32 +00001661 = new_stack + new_stk_szb
sewardj1e8cdc92002-04-18 11:37:52 +00001662 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; /* -4 ??? */;
sewardje663cb92002-04-12 10:26:32 +00001663 }
sewardj1e8cdc92002-04-18 11:37:52 +00001664
1665 vg_threads[tid].m_esp
1666 = vg_threads[tid].stack_base
1667 + vg_threads[tid].stack_size
1668 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB;
1669
sewardje663cb92002-04-12 10:26:32 +00001670 if (VG_(clo_instrument))
1671 VGM_(make_noaccess)( vg_threads[tid].m_esp,
1672 VG_AR_CLIENT_STACKBASE_REDZONE_SZB );
1673
1674 /* push arg */
1675 vg_threads[tid].m_esp -= 4;
1676 * (UInt*)(vg_threads[tid].m_esp) = (UInt)arg;
1677
1678 /* push (magical) return address */
1679 vg_threads[tid].m_esp -= 4;
sewardjbc5b99f2002-04-13 00:08:51 +00001680 * (UInt*)(vg_threads[tid].m_esp) = (UInt)VG_(pthreadreturn_bogusRA);
sewardje663cb92002-04-12 10:26:32 +00001681
1682 if (VG_(clo_instrument))
1683 VGM_(make_readable)( vg_threads[tid].m_esp, 2 * 4 );
1684
1685 /* this is where we start */
1686 vg_threads[tid].m_eip = (UInt)start_routine;
1687
sewardj8937c812002-04-12 20:12:20 +00001688 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001689 VG_(sprintf)(msg_buf,
1690 "new thread, created by %d", parent_tid );
1691 print_sched_event(tid, msg_buf);
1692 }
1693
1694 /* store the thread id in *thread. */
1695 // if (VG_(clo_instrument))
1696 // ***** CHECK *thread is writable
1697 *thread = (pthread_t)tid;
1698
sewardj3b5d8862002-04-20 13:53:23 +00001699 vg_threads[tid].associated_mx = NULL;
1700 vg_threads[tid].associated_cv = NULL;
1701 vg_threads[tid].joiner = VG_INVALID_THREADID;
1702 vg_threads[tid].status = VgTs_Runnable;
sewardj604ec3c2002-04-18 22:38:41 +00001703
sewardj5f07b662002-04-23 16:52:51 +00001704 for (i = 0; i < VG_N_THREAD_KEYS; i++)
1705 vg_threads[tid].specifics[i] = NULL;
1706
sewardj604ec3c2002-04-18 22:38:41 +00001707 /* return zero */
sewardje663cb92002-04-12 10:26:32 +00001708 vg_threads[tid].m_edx = 0; /* success */
1709}
1710
1711
sewardj604ec3c2002-04-18 22:38:41 +00001712/* -----------------------------------------------------------
1713 MUTEXes
1714 -------------------------------------------------------- */
1715
sewardj604ec3c2002-04-18 22:38:41 +00001716/* pthread_mutex_t is a struct with at 5 words:
sewardje663cb92002-04-12 10:26:32 +00001717 typedef struct
1718 {
1719 int __m_reserved; -- Reserved for future use
1720 int __m_count; -- Depth of recursive locking
1721 _pthread_descr __m_owner; -- Owner thread (if recursive or errcheck)
1722 int __m_kind; -- Mutex kind: fast, recursive or errcheck
1723 struct _pthread_fastlock __m_lock; -- Underlying fast lock
1724 } pthread_mutex_t;
sewardj604ec3c2002-04-18 22:38:41 +00001725
sewardj6072c362002-04-19 14:40:57 +00001726 #define PTHREAD_MUTEX_INITIALIZER \
1727 {0, 0, 0, PTHREAD_MUTEX_TIMED_NP, __LOCK_INITIALIZER}
1728 # define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
1729 {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER}
1730 # define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \
1731 {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, __LOCK_INITIALIZER}
1732 # define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \
1733 {0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, __LOCK_INITIALIZER}
sewardj604ec3c2002-04-18 22:38:41 +00001734
sewardj6072c362002-04-19 14:40:57 +00001735 How we use it:
sewardj604ec3c2002-04-18 22:38:41 +00001736
sewardj6072c362002-04-19 14:40:57 +00001737 __m_kind never changes and indicates whether or not it is recursive.
1738
1739 __m_count indicates the lock count; if 0, the mutex is not owned by
1740 anybody.
1741
1742 __m_owner has a ThreadId value stuffed into it. We carefully arrange
1743 that ThreadId == 0 is invalid (VG_INVALID_THREADID), so that
1744 statically initialised mutexes correctly appear
1745 to belong to nobody.
1746
1747 In summary, a not-in-use mutex is distinguised by having __m_owner
1748 == 0 (VG_INVALID_THREADID) and __m_count == 0 too. If one of those
1749 conditions holds, the other should too.
1750
1751 There is no linked list of threads waiting for this mutex. Instead
1752 a thread in WaitMX state points at the mutex with its waited_on_mx
1753 field. This makes _unlock() inefficient, but simple to implement the
1754 right semantics viz-a-viz signals.
sewardje663cb92002-04-12 10:26:32 +00001755
sewardj604ec3c2002-04-18 22:38:41 +00001756 We don't have to deal with mutex initialisation; the client side
sewardj6072c362002-04-19 14:40:57 +00001757 deals with that for us.
1758*/
sewardje663cb92002-04-12 10:26:32 +00001759
sewardj3b5d8862002-04-20 13:53:23 +00001760/* Helper fns ... */
1761static
1762void release_one_thread_waiting_on_mutex ( pthread_mutex_t* mutex,
1763 Char* caller )
1764{
1765 Int i;
1766 Char msg_buf[100];
1767
1768 /* Find some arbitrary thread waiting on this mutex, and make it
1769 runnable. If none are waiting, mark the mutex as not held. */
1770 for (i = 1; i < VG_N_THREADS; i++) {
1771 if (vg_threads[i].status == VgTs_Empty)
1772 continue;
1773 if (vg_threads[i].status == VgTs_WaitMX
1774 && vg_threads[i].associated_mx == mutex)
1775 break;
1776 }
1777
1778 vg_assert(i <= VG_N_THREADS);
1779 if (i == VG_N_THREADS) {
1780 /* Nobody else is waiting on it. */
1781 mutex->__m_count = 0;
1782 mutex->__m_owner = VG_INVALID_THREADID;
1783 } else {
1784 /* Notionally transfer the hold to thread i, whose
1785 pthread_mutex_lock() call now returns with 0 (success). */
1786 /* The .count is already == 1. */
1787 vg_assert(vg_threads[i].associated_mx == mutex);
1788 mutex->__m_owner = (_pthread_descr)i;
1789 vg_threads[i].status = VgTs_Runnable;
1790 vg_threads[i].associated_mx = NULL;
sewardj5f07b662002-04-23 16:52:51 +00001791 /* m_edx already holds pth_mx_lock() success (0) */
sewardj3b5d8862002-04-20 13:53:23 +00001792
1793 if (VG_(clo_trace_pthread_level) >= 1) {
1794 VG_(sprintf)(msg_buf, "%s mx %p: RESUME",
1795 caller, mutex );
1796 print_pthread_event(i, msg_buf);
1797 }
1798 }
1799}
1800
sewardje663cb92002-04-12 10:26:32 +00001801
1802static
sewardj30671ff2002-04-21 00:13:57 +00001803void do_pthread_mutex_lock( ThreadId tid,
1804 Bool is_trylock,
sewardjd7fd4d22002-04-24 01:57:27 +00001805 void* /* pthread_mutex_t* */ mutexV )
sewardje663cb92002-04-12 10:26:32 +00001806{
sewardj30671ff2002-04-21 00:13:57 +00001807 Char msg_buf[100];
1808 Char* caller
1809 = is_trylock ? "pthread_mutex_lock "
1810 : "pthread_mutex_trylock";
sewardje663cb92002-04-12 10:26:32 +00001811
sewardjd7fd4d22002-04-24 01:57:27 +00001812 pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
1813
sewardj604ec3c2002-04-18 22:38:41 +00001814 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj30671ff2002-04-21 00:13:57 +00001815 VG_(sprintf)(msg_buf, "%s mx %p ...", caller, mutex );
sewardj604ec3c2002-04-18 22:38:41 +00001816 print_pthread_event(tid, msg_buf);
1817 }
1818
1819 /* Paranoia ... */
1820 vg_assert(is_valid_tid(tid)
1821 && vg_threads[tid].status == VgTs_Runnable);
sewardje663cb92002-04-12 10:26:32 +00001822
1823 /* POSIX doesn't mandate this, but for sanity ... */
1824 if (mutex == NULL) {
1825 vg_threads[tid].m_edx = EINVAL;
1826 return;
1827 }
1828
sewardj604ec3c2002-04-18 22:38:41 +00001829 /* More paranoia ... */
1830 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00001831# ifndef GLIBC_2_1
sewardj604ec3c2002-04-18 22:38:41 +00001832 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00001833 case PTHREAD_MUTEX_ADAPTIVE_NP:
1834# endif
sewardj604ec3c2002-04-18 22:38:41 +00001835 case PTHREAD_MUTEX_RECURSIVE_NP:
1836 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj604ec3c2002-04-18 22:38:41 +00001837 if (mutex->__m_count >= 0) break;
1838 /* else fall thru */
1839 default:
1840 vg_threads[tid].m_edx = EINVAL;
1841 return;
sewardje663cb92002-04-12 10:26:32 +00001842 }
1843
sewardj604ec3c2002-04-18 22:38:41 +00001844 if (mutex->__m_count > 0) {
sewardje663cb92002-04-12 10:26:32 +00001845
sewardj604ec3c2002-04-18 22:38:41 +00001846 vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
sewardjf8f819e2002-04-17 23:21:37 +00001847
1848 /* Someone has it already. */
sewardj604ec3c2002-04-18 22:38:41 +00001849 if ((ThreadId)mutex->__m_owner == tid) {
sewardjf8f819e2002-04-17 23:21:37 +00001850 /* It's locked -- by me! */
sewardj604ec3c2002-04-18 22:38:41 +00001851 if (mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP) {
sewardjf8f819e2002-04-17 23:21:37 +00001852 /* return 0 (success). */
sewardj604ec3c2002-04-18 22:38:41 +00001853 mutex->__m_count++;
sewardjf8f819e2002-04-17 23:21:37 +00001854 vg_threads[tid].m_edx = 0;
sewardj3b5d8862002-04-20 13:53:23 +00001855 VG_(printf)("!!!!!! tid %d, mx %p -> locked %d\n",
sewardj604ec3c2002-04-18 22:38:41 +00001856 tid, mutex, mutex->__m_count);
sewardjf8f819e2002-04-17 23:21:37 +00001857 return;
1858 } else {
sewardj30671ff2002-04-21 00:13:57 +00001859 if (is_trylock)
1860 vg_threads[tid].m_edx = EBUSY;
1861 else
1862 vg_threads[tid].m_edx = EDEADLK;
sewardjf8f819e2002-04-17 23:21:37 +00001863 return;
1864 }
1865 } else {
sewardj6072c362002-04-19 14:40:57 +00001866 /* Someone else has it; we have to wait. Mark ourselves
1867 thusly. */
sewardj05553872002-04-20 20:53:17 +00001868 /* GUARD: __m_count > 0 && __m_owner is valid */
sewardj30671ff2002-04-21 00:13:57 +00001869 if (is_trylock) {
1870 /* caller is polling; so return immediately. */
1871 vg_threads[tid].m_edx = EBUSY;
1872 } else {
1873 vg_threads[tid].status = VgTs_WaitMX;
1874 vg_threads[tid].associated_mx = mutex;
sewardj5f07b662002-04-23 16:52:51 +00001875 vg_threads[tid].m_edx = 0; /* pth_mx_lock success value */
sewardj30671ff2002-04-21 00:13:57 +00001876 if (VG_(clo_trace_pthread_level) >= 1) {
1877 VG_(sprintf)(msg_buf, "%s mx %p: BLOCK",
1878 caller, mutex );
1879 print_pthread_event(tid, msg_buf);
1880 }
1881 }
sewardje663cb92002-04-12 10:26:32 +00001882 return;
1883 }
sewardjf8f819e2002-04-17 23:21:37 +00001884
sewardje663cb92002-04-12 10:26:32 +00001885 } else {
sewardj6072c362002-04-19 14:40:57 +00001886 /* Nobody owns it. Sanity check ... */
1887 vg_assert(mutex->__m_owner == VG_INVALID_THREADID);
sewardjf8f819e2002-04-17 23:21:37 +00001888 /* We get it! [for the first time]. */
sewardj604ec3c2002-04-18 22:38:41 +00001889 mutex->__m_count = 1;
1890 mutex->__m_owner = (_pthread_descr)tid;
sewardj3b5d8862002-04-20 13:53:23 +00001891 vg_assert(vg_threads[tid].associated_mx == NULL);
sewardje663cb92002-04-12 10:26:32 +00001892 /* return 0 (success). */
1893 vg_threads[tid].m_edx = 0;
1894 }
sewardjf8f819e2002-04-17 23:21:37 +00001895
sewardje663cb92002-04-12 10:26:32 +00001896}
1897
1898
1899static
1900void do_pthread_mutex_unlock ( ThreadId tid,
sewardjd7fd4d22002-04-24 01:57:27 +00001901 void* /* pthread_mutex_t* */ mutexV )
sewardje663cb92002-04-12 10:26:32 +00001902{
sewardj3b5d8862002-04-20 13:53:23 +00001903 Char msg_buf[100];
sewardjd7fd4d22002-04-24 01:57:27 +00001904 pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
sewardje663cb92002-04-12 10:26:32 +00001905
sewardj45b4b372002-04-16 22:50:32 +00001906 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj3b5d8862002-04-20 13:53:23 +00001907 VG_(sprintf)(msg_buf, "pthread_mutex_unlock mx %p ...", mutex );
sewardj8937c812002-04-12 20:12:20 +00001908 print_pthread_event(tid, msg_buf);
1909 }
1910
sewardj604ec3c2002-04-18 22:38:41 +00001911 /* Paranoia ... */
1912 vg_assert(is_valid_tid(tid)
1913 && vg_threads[tid].status == VgTs_Runnable);
1914
1915 if (mutex == NULL) {
1916 vg_threads[tid].m_edx = EINVAL;
1917 return;
1918 }
1919
1920 /* More paranoia ... */
1921 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00001922# ifndef GLIBC_2_1
sewardj604ec3c2002-04-18 22:38:41 +00001923 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00001924 case PTHREAD_MUTEX_ADAPTIVE_NP:
1925# endif
sewardj604ec3c2002-04-18 22:38:41 +00001926 case PTHREAD_MUTEX_RECURSIVE_NP:
1927 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj604ec3c2002-04-18 22:38:41 +00001928 if (mutex->__m_count >= 0) break;
1929 /* else fall thru */
1930 default:
1931 vg_threads[tid].m_edx = EINVAL;
1932 return;
1933 }
sewardje663cb92002-04-12 10:26:32 +00001934
1935 /* Barf if we don't currently hold the mutex. */
sewardj604ec3c2002-04-18 22:38:41 +00001936 if (mutex->__m_count == 0 /* nobody holds it */
1937 || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) {
sewardje663cb92002-04-12 10:26:32 +00001938 vg_threads[tid].m_edx = EPERM;
1939 return;
1940 }
1941
sewardjf8f819e2002-04-17 23:21:37 +00001942 /* If it's a multiply-locked recursive mutex, just decrement the
1943 lock count and return. */
sewardj604ec3c2002-04-18 22:38:41 +00001944 if (mutex->__m_count > 1) {
1945 vg_assert(mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP);
1946 mutex->__m_count --;
sewardjf8f819e2002-04-17 23:21:37 +00001947 vg_threads[tid].m_edx = 0; /* success */
1948 return;
1949 }
1950
sewardj604ec3c2002-04-18 22:38:41 +00001951 /* Now we're sure it is locked exactly once, and by the thread who
sewardjf8f819e2002-04-17 23:21:37 +00001952 is now doing an unlock on it. */
sewardj604ec3c2002-04-18 22:38:41 +00001953 vg_assert(mutex->__m_count == 1);
sewardj6072c362002-04-19 14:40:57 +00001954 vg_assert((ThreadId)mutex->__m_owner == tid);
sewardjf8f819e2002-04-17 23:21:37 +00001955
sewardj3b5d8862002-04-20 13:53:23 +00001956 /* Release at max one thread waiting on this mutex. */
1957 release_one_thread_waiting_on_mutex ( mutex, "pthread_mutex_lock" );
sewardje663cb92002-04-12 10:26:32 +00001958
sewardj3b5d8862002-04-20 13:53:23 +00001959 /* Our (tid's) pth_unlock() returns with 0 (success). */
sewardje663cb92002-04-12 10:26:32 +00001960 vg_threads[tid].m_edx = 0; /* Success. */
1961}
1962
1963
sewardj6072c362002-04-19 14:40:57 +00001964/* -----------------------------------------------------------
1965 CONDITION VARIABLES
1966 -------------------------------------------------------- */
sewardje663cb92002-04-12 10:26:32 +00001967
sewardj6072c362002-04-19 14:40:57 +00001968/* The relevant native types are as follows:
1969 (copied from /usr/include/bits/pthreadtypes.h)
sewardj77e466c2002-04-14 02:29:29 +00001970
sewardj6072c362002-04-19 14:40:57 +00001971 -- Conditions (not abstract because of PTHREAD_COND_INITIALIZER
1972 typedef struct
1973 {
1974 struct _pthread_fastlock __c_lock; -- Protect against concurrent access
1975 _pthread_descr __c_waiting; -- Threads waiting on this condition
1976 } pthread_cond_t;
sewardj77e466c2002-04-14 02:29:29 +00001977
sewardj6072c362002-04-19 14:40:57 +00001978 -- Attribute for conditionally variables.
1979 typedef struct
1980 {
1981 int __dummy;
1982 } pthread_condattr_t;
sewardj77e466c2002-04-14 02:29:29 +00001983
sewardj6072c362002-04-19 14:40:57 +00001984 #define PTHREAD_COND_INITIALIZER {__LOCK_INITIALIZER, 0}
sewardj77e466c2002-04-14 02:29:29 +00001985
sewardj3b5d8862002-04-20 13:53:23 +00001986 We don't use any fields of pthread_cond_t for anything at all.
1987 Only the identity of the CVs is important.
sewardj6072c362002-04-19 14:40:57 +00001988
1989 Linux pthreads supports no attributes on condition variables, so we
sewardj3b5d8862002-04-20 13:53:23 +00001990 don't need to think too hard there. */
sewardj6072c362002-04-19 14:40:57 +00001991
sewardj77e466c2002-04-14 02:29:29 +00001992
sewardj5f07b662002-04-23 16:52:51 +00001993static
1994void do_pthread_cond_timedwait_TIMEOUT ( ThreadId tid )
1995{
1996 Char msg_buf[100];
1997 pthread_mutex_t* mx;
1998 pthread_cond_t* cv;
1999
2000 vg_assert(is_valid_tid(tid)
2001 && vg_threads[tid].status == VgTs_WaitCV
2002 && vg_threads[tid].awaken_at != 0xFFFFFFFF);
2003 mx = vg_threads[tid].associated_mx;
2004 vg_assert(mx != NULL);
2005 cv = vg_threads[tid].associated_cv;
2006 vg_assert(cv != NULL);
2007
2008 if (mx->__m_owner == VG_INVALID_THREADID) {
2009 /* Currently unheld; hand it out to thread tid. */
2010 vg_assert(mx->__m_count == 0);
2011 vg_threads[tid].status = VgTs_Runnable;
2012 vg_threads[tid].m_edx = ETIMEDOUT;
2013 /* pthread_cond_wait return value */
2014 vg_threads[tid].associated_cv = NULL;
2015 vg_threads[tid].associated_mx = NULL;
2016 mx->__m_owner = (_pthread_descr)tid;
2017 mx->__m_count = 1;
2018
2019 if (VG_(clo_trace_pthread_level) >= 1) {
2020 VG_(sprintf)(msg_buf, "pthread_cond_timedwai cv %p: TIMEOUT with mx %p",
2021 cv, mx );
2022 print_pthread_event(tid, msg_buf);
2023 }
2024 } else {
2025 /* Currently held. Make thread tid be blocked on it. */
2026 vg_assert(mx->__m_count > 0);
2027 vg_threads[tid].status = VgTs_WaitMX;
2028 vg_threads[tid].m_edx = ETIMEDOUT;
2029 /* pthread_cond_wait return value */
2030 vg_threads[tid].associated_cv = NULL;
2031 vg_threads[tid].associated_mx = mx;
2032 if (VG_(clo_trace_pthread_level) >= 1) {
2033 VG_(sprintf)(msg_buf,
2034 "pthread_cond_timedwai cv %p: TIMEOUT -> BLOCK for mx %p",
2035 cv, mx );
2036 print_pthread_event(tid, msg_buf);
2037 }
2038
2039 }
2040}
2041
2042
sewardj3b5d8862002-04-20 13:53:23 +00002043static
2044void release_N_threads_waiting_on_cond ( pthread_cond_t* cond,
2045 Int n_to_release,
2046 Char* caller )
2047{
2048 Int i;
2049 Char msg_buf[100];
2050 pthread_mutex_t* mx;
2051
2052 while (True) {
2053 if (n_to_release == 0)
2054 return;
2055
2056 /* Find a thread waiting on this CV. */
2057 for (i = 1; i < VG_N_THREADS; i++) {
2058 if (vg_threads[i].status == VgTs_Empty)
2059 continue;
2060 if (vg_threads[i].status == VgTs_WaitCV
2061 && vg_threads[i].associated_cv == cond)
2062 break;
2063 }
2064 vg_assert(i <= VG_N_THREADS);
2065
2066 if (i == VG_N_THREADS) {
2067 /* Nobody else is waiting on it. */
2068 return;
2069 }
2070
2071 mx = vg_threads[i].associated_mx;
2072 vg_assert(mx != NULL);
2073
2074 if (mx->__m_owner == VG_INVALID_THREADID) {
2075 /* Currently unheld; hand it out to thread i. */
2076 vg_assert(mx->__m_count == 0);
2077 vg_threads[i].status = VgTs_Runnable;
2078 vg_threads[i].associated_cv = NULL;
2079 vg_threads[i].associated_mx = NULL;
2080 mx->__m_owner = (_pthread_descr)i;
2081 mx->__m_count = 1;
sewardj5f07b662002-04-23 16:52:51 +00002082 /* .m_edx already holds pth_cond_wait success value (0) */
sewardj3b5d8862002-04-20 13:53:23 +00002083
2084 if (VG_(clo_trace_pthread_level) >= 1) {
2085 VG_(sprintf)(msg_buf, "%s cv %p: RESUME with mx %p",
2086 caller, cond, mx );
2087 print_pthread_event(i, msg_buf);
2088 }
2089
2090 } else {
2091 /* Currently held. Make thread i be blocked on it. */
sewardj5f07b662002-04-23 16:52:51 +00002092 vg_assert(mx->__m_count > 0);
sewardj3b5d8862002-04-20 13:53:23 +00002093 vg_threads[i].status = VgTs_WaitMX;
2094 vg_threads[i].associated_cv = NULL;
2095 vg_threads[i].associated_mx = mx;
sewardj5f07b662002-04-23 16:52:51 +00002096 vg_threads[i].m_edx = 0; /* pth_cond_wait success value */
sewardj3b5d8862002-04-20 13:53:23 +00002097
2098 if (VG_(clo_trace_pthread_level) >= 1) {
2099 VG_(sprintf)(msg_buf, "%s cv %p: BLOCK for mx %p",
2100 caller, cond, mx );
2101 print_pthread_event(i, msg_buf);
2102 }
2103
2104 }
2105
2106 n_to_release--;
2107 }
2108}
2109
2110
2111static
2112void do_pthread_cond_wait ( ThreadId tid,
2113 pthread_cond_t *cond,
sewardj5f07b662002-04-23 16:52:51 +00002114 pthread_mutex_t *mutex,
2115 UInt ms_end )
sewardj3b5d8862002-04-20 13:53:23 +00002116{
2117 Char msg_buf[100];
2118
sewardj5f07b662002-04-23 16:52:51 +00002119 /* If ms_end == 0xFFFFFFFF, wait forever (no timeout). Otherwise,
2120 ms_end is the ending millisecond. */
2121
sewardj3b5d8862002-04-20 13:53:23 +00002122 /* pre: mutex should be a valid mutex and owned by tid. */
2123 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj5f07b662002-04-23 16:52:51 +00002124 VG_(sprintf)(msg_buf, "pthread_cond_wait cv %p, mx %p, end %d ...",
2125 cond, mutex, ms_end );
sewardj3b5d8862002-04-20 13:53:23 +00002126 print_pthread_event(tid, msg_buf);
2127 }
2128
2129 /* Paranoia ... */
2130 vg_assert(is_valid_tid(tid)
2131 && vg_threads[tid].status == VgTs_Runnable);
2132
2133 if (mutex == NULL || cond == NULL) {
2134 vg_threads[tid].m_edx = EINVAL;
2135 return;
2136 }
2137
2138 /* More paranoia ... */
2139 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00002140# ifndef GLIBC_2_1
sewardj3b5d8862002-04-20 13:53:23 +00002141 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00002142 case PTHREAD_MUTEX_ADAPTIVE_NP:
2143# endif
sewardj3b5d8862002-04-20 13:53:23 +00002144 case PTHREAD_MUTEX_RECURSIVE_NP:
2145 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj3b5d8862002-04-20 13:53:23 +00002146 if (mutex->__m_count >= 0) break;
2147 /* else fall thru */
2148 default:
2149 vg_threads[tid].m_edx = EINVAL;
2150 return;
2151 }
2152
2153 /* Barf if we don't currently hold the mutex. */
2154 if (mutex->__m_count == 0 /* nobody holds it */
2155 || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) {
2156 vg_threads[tid].m_edx = EINVAL;
2157 return;
2158 }
2159
2160 /* Queue ourselves on the condition. */
2161 vg_threads[tid].status = VgTs_WaitCV;
2162 vg_threads[tid].associated_cv = cond;
2163 vg_threads[tid].associated_mx = mutex;
sewardj5f07b662002-04-23 16:52:51 +00002164 vg_threads[tid].awaken_at = ms_end;
sewardj3b5d8862002-04-20 13:53:23 +00002165
2166 if (VG_(clo_trace_pthread_level) >= 1) {
2167 VG_(sprintf)(msg_buf,
2168 "pthread_cond_wait cv %p, mx %p: BLOCK",
2169 cond, mutex );
2170 print_pthread_event(tid, msg_buf);
2171 }
2172
2173 /* Release the mutex. */
2174 release_one_thread_waiting_on_mutex ( mutex, "pthread_cond_wait " );
2175}
2176
2177
2178static
2179void do_pthread_cond_signal_or_broadcast ( ThreadId tid,
2180 Bool broadcast,
2181 pthread_cond_t *cond )
2182{
2183 Char msg_buf[100];
2184 Char* caller
2185 = broadcast ? "pthread_cond_broadcast"
2186 : "pthread_cond_signal ";
2187
2188 if (VG_(clo_trace_pthread_level) >= 2) {
2189 VG_(sprintf)(msg_buf, "%s cv %p ...",
2190 caller, cond );
2191 print_pthread_event(tid, msg_buf);
2192 }
2193
2194 /* Paranoia ... */
2195 vg_assert(is_valid_tid(tid)
2196 && vg_threads[tid].status == VgTs_Runnable);
2197
2198 if (cond == NULL) {
2199 vg_threads[tid].m_edx = EINVAL;
2200 return;
2201 }
2202
2203 release_N_threads_waiting_on_cond (
2204 cond,
2205 broadcast ? VG_N_THREADS : 1,
2206 caller
2207 );
2208
2209 vg_threads[tid].m_edx = 0; /* success */
2210}
2211
sewardj77e466c2002-04-14 02:29:29 +00002212
sewardj5f07b662002-04-23 16:52:51 +00002213/* -----------------------------------------------------------
2214 THREAD SPECIFIC DATA
2215 -------------------------------------------------------- */
2216
2217static __inline__
2218Bool is_valid_key ( ThreadKey k )
2219{
2220 /* k unsigned; hence no < 0 check */
2221 if (k >= VG_N_THREAD_KEYS) return False;
2222 if (!vg_thread_keys[k].inuse) return False;
2223 return True;
2224}
2225
2226static
2227void do_pthread_key_create ( ThreadId tid,
2228 pthread_key_t* key,
2229 void (*destructor)(void*) )
2230{
2231 Int i;
2232 Char msg_buf[100];
2233
2234 if (VG_(clo_trace_pthread_level) >= 1) {
2235 VG_(sprintf)(msg_buf, "pthread_key_create *key %p, destr %p",
2236 key, destructor );
2237 print_pthread_event(tid, msg_buf);
2238 }
2239
2240 vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
2241 vg_assert(is_valid_tid(tid)
2242 && vg_threads[tid].status == VgTs_Runnable);
2243
2244 for (i = 0; i < VG_N_THREAD_KEYS; i++)
2245 if (!vg_thread_keys[i].inuse)
2246 break;
2247
2248 if (i == VG_N_THREAD_KEYS) {
2249 /* vg_threads[tid].m_edx = EAGAIN;
2250 return;
2251 */
2252 VG_(panic)("pthread_key_create: VG_N_THREAD_KEYS is too low;"
2253 " increase and recompile");
2254 }
2255
2256 vg_thread_keys[i].inuse = True;
2257 /* TODO: check key for addressibility */
2258 *key = i;
2259 vg_threads[tid].m_edx = 0;
2260}
2261
2262
2263static
2264void do_pthread_key_delete ( ThreadId tid, pthread_key_t key )
2265{
2266 Char msg_buf[100];
2267 if (VG_(clo_trace_pthread_level) >= 1) {
2268 VG_(sprintf)(msg_buf, "pthread_key_delete key %d",
2269 key );
2270 print_pthread_event(tid, msg_buf);
2271 }
2272
2273 vg_assert(is_valid_tid(tid)
2274 && vg_threads[tid].status == VgTs_Runnable);
2275
2276 if (!is_valid_key(key)) {
2277 vg_threads[tid].m_edx = EINVAL;
2278 return;
2279 }
2280
2281 vg_thread_keys[key].inuse = False;
2282
2283 /* Optional. We're not required to do this, although it shouldn't
2284 make any difference to programs which use the key/specifics
2285 functions correctly. */
sewardj3b13f0e2002-04-25 20:17:29 +00002286# if 1
sewardj5f07b662002-04-23 16:52:51 +00002287 for (tid = 1; tid < VG_N_THREADS; tid++) {
2288 if (vg_threads[tid].status != VgTs_Empty)
2289 vg_threads[tid].specifics[key] = NULL;
2290 }
sewardj3b13f0e2002-04-25 20:17:29 +00002291# endif
sewardj5f07b662002-04-23 16:52:51 +00002292}
2293
2294
2295static
2296void do_pthread_getspecific ( ThreadId tid, pthread_key_t key )
2297{
2298 Char msg_buf[100];
2299 if (VG_(clo_trace_pthread_level) >= 1) {
2300 VG_(sprintf)(msg_buf, "pthread_getspecific key %d",
2301 key );
2302 print_pthread_event(tid, msg_buf);
2303 }
2304
2305 vg_assert(is_valid_tid(tid)
2306 && vg_threads[tid].status == VgTs_Runnable);
2307
2308 if (!is_valid_key(key)) {
2309 vg_threads[tid].m_edx = (UInt)NULL;
2310 return;
2311 }
2312
2313 vg_threads[tid].m_edx = (UInt)vg_threads[tid].specifics[key];
2314}
2315
2316
2317static
2318void do_pthread_setspecific ( ThreadId tid,
2319 pthread_key_t key,
2320 void *pointer )
2321{
2322 Char msg_buf[100];
2323 if (VG_(clo_trace_pthread_level) >= 1) {
2324 VG_(sprintf)(msg_buf, "pthread_setspecific key %d, ptr %p",
2325 key, pointer );
2326 print_pthread_event(tid, msg_buf);
2327 }
2328
2329 vg_assert(is_valid_tid(tid)
2330 && vg_threads[tid].status == VgTs_Runnable);
2331
2332 if (!is_valid_key(key)) {
2333 vg_threads[tid].m_edx = EINVAL;
2334 return;
2335 }
2336
2337 vg_threads[tid].specifics[key] = pointer;
2338 vg_threads[tid].m_edx = 0;
2339}
2340
2341
sewardje663cb92002-04-12 10:26:32 +00002342/* ---------------------------------------------------------------------
2343 Handle non-trivial client requests.
2344 ------------------------------------------------------------------ */
2345
2346static
2347void do_nontrivial_clientreq ( ThreadId tid )
2348{
2349 UInt* arg = (UInt*)(vg_threads[tid].m_eax);
2350 UInt req_no = arg[0];
2351 switch (req_no) {
2352
2353 case VG_USERREQ__PTHREAD_CREATE:
2354 do_pthread_create( tid,
2355 (pthread_t*)arg[1],
2356 (pthread_attr_t*)arg[2],
2357 (void*(*)(void*))arg[3],
2358 (void*)arg[4] );
2359 break;
2360
sewardjbc5b99f2002-04-13 00:08:51 +00002361 case VG_USERREQ__PTHREAD_RETURNS:
2362 handle_pthread_return( tid, (void*)arg[1] );
sewardje663cb92002-04-12 10:26:32 +00002363 break;
2364
2365 case VG_USERREQ__PTHREAD_JOIN:
2366 do_pthread_join( tid, arg[1], (void**)(arg[2]) );
2367 break;
2368
sewardje663cb92002-04-12 10:26:32 +00002369 case VG_USERREQ__PTHREAD_CANCEL:
2370 do_pthread_cancel( tid, (pthread_t)(arg[1]) );
2371 break;
2372
sewardj3b5d8862002-04-20 13:53:23 +00002373 case VG_USERREQ__PTHREAD_EXIT:
2374 do_pthread_exit( tid, (void*)(arg[1]) );
2375 break;
2376
2377 case VG_USERREQ__PTHREAD_COND_WAIT:
2378 do_pthread_cond_wait( tid,
2379 (pthread_cond_t *)(arg[1]),
sewardj5f07b662002-04-23 16:52:51 +00002380 (pthread_mutex_t *)(arg[2]),
2381 0xFFFFFFFF /* no timeout */ );
2382 break;
2383
2384 case VG_USERREQ__PTHREAD_COND_TIMEDWAIT:
2385 do_pthread_cond_wait( tid,
2386 (pthread_cond_t *)(arg[1]),
2387 (pthread_mutex_t *)(arg[2]),
2388 arg[3] /* timeout millisecond point */ );
sewardj3b5d8862002-04-20 13:53:23 +00002389 break;
2390
2391 case VG_USERREQ__PTHREAD_COND_SIGNAL:
2392 do_pthread_cond_signal_or_broadcast(
2393 tid,
2394 False, /* signal, not broadcast */
2395 (pthread_cond_t *)(arg[1]) );
2396 break;
2397
2398 case VG_USERREQ__PTHREAD_COND_BROADCAST:
2399 do_pthread_cond_signal_or_broadcast(
2400 tid,
2401 True, /* broadcast, not signal */
2402 (pthread_cond_t *)(arg[1]) );
2403 break;
2404
sewardj5f07b662002-04-23 16:52:51 +00002405 case VG_USERREQ__PTHREAD_KEY_CREATE:
2406 do_pthread_key_create ( tid,
2407 (pthread_key_t*)(arg[1]),
2408 (void(*)(void*))(arg[2]) );
2409 break;
2410
2411 case VG_USERREQ__PTHREAD_KEY_DELETE:
2412 do_pthread_key_delete ( tid,
2413 (pthread_key_t)(arg[1]) );
2414 break;
2415
sewardj5f07b662002-04-23 16:52:51 +00002416 case VG_USERREQ__PTHREAD_SETSPECIFIC:
2417 do_pthread_setspecific ( tid,
2418 (pthread_key_t)(arg[1]),
2419 (void*)(arg[2]) );
2420 break;
2421
sewardje663cb92002-04-12 10:26:32 +00002422 case VG_USERREQ__MAKE_NOACCESS:
2423 case VG_USERREQ__MAKE_WRITABLE:
2424 case VG_USERREQ__MAKE_READABLE:
2425 case VG_USERREQ__DISCARD:
2426 case VG_USERREQ__CHECK_WRITABLE:
2427 case VG_USERREQ__CHECK_READABLE:
2428 case VG_USERREQ__MAKE_NOACCESS_STACK:
2429 case VG_USERREQ__RUNNING_ON_VALGRIND:
2430 case VG_USERREQ__DO_LEAK_CHECK:
sewardj8c824512002-04-14 04:16:48 +00002431 vg_threads[tid].m_edx
2432 = VG_(handle_client_request) ( &vg_threads[tid], arg );
sewardje663cb92002-04-12 10:26:32 +00002433 break;
2434
sewardj77e466c2002-04-14 02:29:29 +00002435 case VG_USERREQ__SIGNAL_RETURNS:
2436 handle_signal_return(tid);
2437 break;
sewardj54cacf02002-04-12 23:24:59 +00002438
sewardje663cb92002-04-12 10:26:32 +00002439 default:
2440 VG_(printf)("panic'd on private request = 0x%x\n", arg[0] );
2441 VG_(panic)("handle_private_client_pthread_request: "
2442 "unknown request");
2443 /*NOTREACHED*/
2444 break;
2445 }
2446}
2447
2448
sewardj6072c362002-04-19 14:40:57 +00002449/* ---------------------------------------------------------------------
2450 Sanity checking.
2451 ------------------------------------------------------------------ */
2452
2453/* Internal consistency checks on the sched/pthread structures. */
2454static
2455void scheduler_sanity ( void )
2456{
sewardj3b5d8862002-04-20 13:53:23 +00002457 pthread_mutex_t* mx;
2458 pthread_cond_t* cv;
sewardj6072c362002-04-19 14:40:57 +00002459 Int i;
sewardj5f07b662002-04-23 16:52:51 +00002460
sewardj6072c362002-04-19 14:40:57 +00002461 /* VG_(printf)("scheduler_sanity\n"); */
2462 for (i = 1; i < VG_N_THREADS; i++) {
sewardj3b5d8862002-04-20 13:53:23 +00002463 mx = vg_threads[i].associated_mx;
2464 cv = vg_threads[i].associated_cv;
sewardj6072c362002-04-19 14:40:57 +00002465 if (vg_threads[i].status == VgTs_WaitMX) {
sewardj05553872002-04-20 20:53:17 +00002466 /* If we're waiting on a MX: (1) the mx is not null, (2, 3)
2467 it's actually held by someone, since otherwise this thread
2468 is deadlocked, (4) the mutex's owner is not us, since
2469 otherwise this thread is also deadlocked. The logic in
2470 do_pthread_mutex_lock rejects attempts by a thread to lock
2471 a (non-recursive) mutex which it already owns.
2472
2473 (2) has been seen to fail sometimes. I don't know why.
2474 Possibly to do with signals. */
sewardj3b5d8862002-04-20 13:53:23 +00002475 vg_assert(cv == NULL);
sewardj05553872002-04-20 20:53:17 +00002476 /* 1 */ vg_assert(mx != NULL);
2477 /* 2 */ vg_assert(mx->__m_count > 0);
2478 /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
2479 /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner);
sewardj3b5d8862002-04-20 13:53:23 +00002480 } else
2481 if (vg_threads[i].status == VgTs_WaitCV) {
2482 vg_assert(cv != NULL);
2483 vg_assert(mx != NULL);
sewardj6072c362002-04-19 14:40:57 +00002484 } else {
sewardj05553872002-04-20 20:53:17 +00002485 /* Unfortunately these don't hold true when a sighandler is
2486 running. To be fixed. */
2487 /* vg_assert(cv == NULL); */
2488 /* vg_assert(mx == NULL); */
sewardj6072c362002-04-19 14:40:57 +00002489 }
2490 }
sewardj5f07b662002-04-23 16:52:51 +00002491
2492 for (i = 0; i < VG_N_THREAD_KEYS; i++) {
2493 if (!vg_thread_keys[i].inuse)
2494 vg_assert(vg_thread_keys[i].destructor == NULL);
2495 }
sewardj6072c362002-04-19 14:40:57 +00002496}
2497
2498
sewardje663cb92002-04-12 10:26:32 +00002499/*--------------------------------------------------------------------*/
2500/*--- end vg_scheduler.c ---*/
2501/*--------------------------------------------------------------------*/