blob: e9313f68893501b7e9b5a8f6a955af3014c7455f [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. */
sewardj1ffa8da2002-04-26 22:47:57 +0000696
697/* [Helper fn for handle_signal_return] tid, assumed to be in WaitFD
698 for read or write, has been interrupted by a signal. Find and
699 clear the relevant vg_waiting_fd[] entry. Most of the code in this
700 procedure is total paranoia, if you look closely. */
701static
702void cleanup_waiting_fd_table ( ThreadId tid )
703{
704 Int i, waiters;
705
706 vg_assert(is_valid_tid(tid));
707 vg_assert(vg_threads[tid].status == VgTs_WaitFD);
708 vg_assert(vg_threads[tid].m_eax == __NR_read
709 || vg_threads[tid].m_eax == __NR_write);
710
711 /* Excessively paranoidly ... find the fd this op was waiting
712 for, and mark it as not being waited on. */
713 waiters = 0;
714 for (i = 0; i < VG_N_WAITING_FDS; i++) {
715 if (vg_waiting_fds[i].tid == tid) {
716 waiters++;
717 vg_assert(vg_waiting_fds[i].syscall_no == vg_threads[tid].m_eax);
718 }
719 }
720 vg_assert(waiters == 1);
721 for (i = 0; i < VG_N_WAITING_FDS; i++)
722 if (vg_waiting_fds[i].tid == tid)
723 break;
724 vg_assert(i < VG_N_WAITING_FDS);
725 vg_assert(vg_waiting_fds[i].fd != -1);
726 vg_waiting_fds[i].fd = -1; /* not in use */
727}
728
729
sewardj6072c362002-04-19 14:40:57 +0000730static
731void handle_signal_return ( ThreadId tid )
732{
733 Char msg_buf[100];
734 Bool restart_blocked_syscalls;
735
736 vg_assert(is_valid_tid(tid));
737
738 restart_blocked_syscalls = VG_(signal_returns)(tid);
739
740 if (restart_blocked_syscalls)
741 /* Easy; we don't have to do anything. */
742 return;
743
sewardj1ffa8da2002-04-26 22:47:57 +0000744 if (vg_threads[tid].status == VgTs_WaitFD
745 && (vg_threads[tid].m_eax == __NR_read
746 || vg_threads[tid].m_eax == __NR_write)) {
sewardj6072c362002-04-19 14:40:57 +0000747 /* read() or write() interrupted. Force a return with EINTR. */
sewardj1ffa8da2002-04-26 22:47:57 +0000748 cleanup_waiting_fd_table(tid);
sewardj6072c362002-04-19 14:40:57 +0000749 vg_threads[tid].m_eax = -VKI_EINTR;
750 vg_threads[tid].status = VgTs_Runnable;
sewardj1ffa8da2002-04-26 22:47:57 +0000751
sewardj6072c362002-04-19 14:40:57 +0000752 if (VG_(clo_trace_sched)) {
753 VG_(sprintf)(msg_buf,
754 "read() / write() interrupted by signal; return EINTR" );
755 print_sched_event(tid, msg_buf);
756 }
757 return;
758 }
759
sewardj1ffa8da2002-04-26 22:47:57 +0000760 if (vg_threads[tid].status == VgTs_WaitFD
761 && vg_threads[tid].m_eax == __NR_nanosleep) {
sewardj6072c362002-04-19 14:40:57 +0000762 /* We interrupted a nanosleep(). The right thing to do is to
763 write the unused time to nanosleep's second param and return
764 EINTR, but I'm too lazy for that. */
765 return;
766 }
767
sewardj1ffa8da2002-04-26 22:47:57 +0000768 if (vg_threads[tid].status == VgTs_WaitFD) {
769 VG_(panic)("handle_signal_return: unknown interrupted syscall");
770 }
771
sewardj6072c362002-04-19 14:40:57 +0000772 /* All other cases? Just return. */
773}
774
775
sewardje663cb92002-04-12 10:26:32 +0000776static
777void sched_do_syscall ( ThreadId tid )
778{
779 UInt saved_eax;
780 UInt res, syscall_no;
781 UInt fd;
782 Bool might_block, assumed_nonblocking;
783 Bool orig_fd_blockness;
784 Char msg_buf[100];
785
sewardj6072c362002-04-19 14:40:57 +0000786 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +0000787 vg_assert(vg_threads[tid].status == VgTs_Runnable);
788
789 syscall_no = vg_threads[tid].m_eax; /* syscall number */
790
791 if (syscall_no == __NR_nanosleep) {
sewardj5f07b662002-04-23 16:52:51 +0000792 UInt t_now, t_awaken;
sewardje663cb92002-04-12 10:26:32 +0000793 struct vki_timespec* req;
794 req = (struct vki_timespec*)vg_threads[tid].m_ebx; /* arg1 */
sewardj5f07b662002-04-23 16:52:51 +0000795 t_now = VG_(read_millisecond_timer)();
sewardje663cb92002-04-12 10:26:32 +0000796 t_awaken
797 = t_now
sewardj5f07b662002-04-23 16:52:51 +0000798 + (UInt)1000ULL * (UInt)(req->tv_sec)
799 + (UInt)(req->tv_nsec) / 1000000;
sewardje663cb92002-04-12 10:26:32 +0000800 vg_threads[tid].status = VgTs_Sleeping;
801 vg_threads[tid].awaken_at = t_awaken;
sewardj8937c812002-04-12 20:12:20 +0000802 if (VG_(clo_trace_sched)) {
sewardj5f07b662002-04-23 16:52:51 +0000803 VG_(sprintf)(msg_buf, "at %d: nanosleep for %d",
sewardje663cb92002-04-12 10:26:32 +0000804 t_now, t_awaken-t_now);
805 print_sched_event(tid, msg_buf);
806 }
807 /* Force the scheduler to run something else for a while. */
808 return;
809 }
810
811 switch (syscall_no) {
812 case __NR_read:
813 case __NR_write:
814 assumed_nonblocking
815 = False;
816 might_block
817 = fd_is_blockful(vg_threads[tid].m_ebx /* arg1 */);
818 break;
819 default:
820 might_block = False;
821 assumed_nonblocking = True;
822 }
823
824 if (assumed_nonblocking) {
825 /* We think it's non-blocking. Just do it in the normal way. */
826 VG_(perform_assumed_nonblocking_syscall)(tid);
827 /* The thread is still runnable. */
828 return;
829 }
830
831 /* It might block. Take evasive action. */
832 switch (syscall_no) {
833 case __NR_read:
834 case __NR_write:
835 fd = vg_threads[tid].m_ebx; break;
836 default:
837 vg_assert(3+3 == 7);
838 }
839
840 /* Set the fd to nonblocking, and do the syscall, which will return
841 immediately, in order to lodge a request with the Linux kernel.
842 We later poll for I/O completion using select(). */
843
844 orig_fd_blockness = fd_is_blockful(fd);
845 set_fd_nonblocking(fd);
846 vg_assert(!fd_is_blockful(fd));
847 VG_(check_known_blocking_syscall)(tid, syscall_no, NULL /* PRE */);
848
849 /* This trashes the thread's %eax; we have to preserve it. */
850 saved_eax = vg_threads[tid].m_eax;
851 KERNEL_DO_SYSCALL(tid,res);
852
853 /* Restore original blockfulness of the fd. */
854 if (orig_fd_blockness)
855 set_fd_blocking(fd);
856 else
857 set_fd_nonblocking(fd);
858
859 if (res != -VKI_EWOULDBLOCK) {
860 /* It didn't block; it went through immediately. So finish off
861 in the normal way. Don't restore %EAX, since that now
862 (correctly) holds the result of the call. */
863 VG_(check_known_blocking_syscall)(tid, syscall_no, &res /* POST */);
864 /* We're still runnable. */
865 vg_assert(vg_threads[tid].status == VgTs_Runnable);
866
867 } else {
868
869 /* It would have blocked. First, restore %EAX to what it was
870 before our speculative call. */
871 vg_threads[tid].m_eax = saved_eax;
872 /* Put this fd in a table of fds on which we are waiting for
873 completion. The arguments for select() later are constructed
874 from this table. */
875 add_waiting_fd(tid, fd, saved_eax /* which holds the syscall # */);
876 /* Deschedule thread until an I/O completion happens. */
877 vg_threads[tid].status = VgTs_WaitFD;
sewardj8937c812002-04-12 20:12:20 +0000878 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +0000879 VG_(sprintf)(msg_buf,"block until I/O ready on fd %d", fd);
880 print_sched_event(tid, msg_buf);
881 }
882
883 }
884}
885
886
887/* Find out which of the fds in vg_waiting_fds are now ready to go, by
888 making enquiries with select(), and mark them as ready. We have to
889 wait for the requesting threads to fall into the the WaitFD state
890 before we can actually finally deliver the results, so this
891 procedure doesn't do that; complete_blocked_syscalls() does it.
892
893 It might seem odd that a thread which has done a blocking syscall
894 is not in WaitFD state; the way this can happen is if it initially
895 becomes WaitFD, but then a signal is delivered to it, so it becomes
896 Runnable for a while. In this case we have to wait for the
897 sighandler to return, whereupon the WaitFD state is resumed, and
898 only at that point can the I/O result be delivered to it. However,
899 this point may be long after the fd is actually ready.
900
901 So, poll_for_ready_fds() merely detects fds which are ready.
902 complete_blocked_syscalls() does the second half of the trick,
903 possibly much later: it delivers the results from ready fds to
904 threads in WaitFD state.
905*/
sewardj9a199dc2002-04-14 13:01:38 +0000906static
sewardje663cb92002-04-12 10:26:32 +0000907void poll_for_ready_fds ( void )
908{
909 vki_ksigset_t saved_procmask;
910 vki_fd_set readfds;
911 vki_fd_set writefds;
912 vki_fd_set exceptfds;
913 struct vki_timeval timeout;
914 Int fd, fd_max, i, n_ready, syscall_no, n_ok;
915 ThreadId tid;
916 Bool rd_ok, wr_ok, ex_ok;
917 Char msg_buf[100];
918
sewardje462e202002-04-13 04:09:07 +0000919 struct vki_timespec* rem;
sewardj5f07b662002-04-23 16:52:51 +0000920 UInt t_now;
sewardje462e202002-04-13 04:09:07 +0000921
sewardje663cb92002-04-12 10:26:32 +0000922 /* Awaken any sleeping threads whose sleep has expired. */
sewardj6072c362002-04-19 14:40:57 +0000923 for (tid = 1; tid < VG_N_THREADS; tid++)
sewardj853f55d2002-04-26 00:27:53 +0000924 if (vg_threads[tid].status == VgTs_Sleeping)
925 break;
sewardj6072c362002-04-19 14:40:57 +0000926
sewardj5f07b662002-04-23 16:52:51 +0000927 /* Avoid pointless calls to VG_(read_millisecond_timer). */
sewardj6072c362002-04-19 14:40:57 +0000928 if (tid < VG_N_THREADS) {
sewardj5f07b662002-04-23 16:52:51 +0000929 t_now = VG_(read_millisecond_timer)();
sewardj6072c362002-04-19 14:40:57 +0000930 for (tid = 1; tid < VG_N_THREADS; tid++) {
931 if (vg_threads[tid].status != VgTs_Sleeping)
932 continue;
933 if (t_now >= vg_threads[tid].awaken_at) {
934 /* Resume this thread. Set to zero the remaining-time
935 (second) arg of nanosleep, since it's used up all its
936 time. */
937 vg_assert(vg_threads[tid].m_eax == __NR_nanosleep);
938 rem = (struct vki_timespec *)vg_threads[tid].m_ecx; /* arg2 */
939 if (rem != NULL) {
940 rem->tv_sec = 0;
941 rem->tv_nsec = 0;
942 }
943 /* Make the syscall return 0 (success). */
944 vg_threads[tid].m_eax = 0;
945 /* Reschedule this thread. */
946 vg_threads[tid].status = VgTs_Runnable;
947 if (VG_(clo_trace_sched)) {
sewardj5f07b662002-04-23 16:52:51 +0000948 VG_(sprintf)(msg_buf, "at %d: nanosleep done",
sewardj6072c362002-04-19 14:40:57 +0000949 t_now);
950 print_sched_event(tid, msg_buf);
951 }
sewardje663cb92002-04-12 10:26:32 +0000952 }
953 }
954 }
sewardje663cb92002-04-12 10:26:32 +0000955
sewardje462e202002-04-13 04:09:07 +0000956 /* And look for threads waiting on file descriptors which are now
957 ready for I/O.*/
sewardje663cb92002-04-12 10:26:32 +0000958 timeout.tv_sec = 0;
959 timeout.tv_usec = 0;
960
961 VKI_FD_ZERO(&readfds);
962 VKI_FD_ZERO(&writefds);
963 VKI_FD_ZERO(&exceptfds);
964 fd_max = -1;
965 for (i = 0; i < VG_N_WAITING_FDS; i++) {
966 if (vg_waiting_fds[i].fd == -1 /* not in use */)
967 continue;
968 if (vg_waiting_fds[i].ready /* already ready? */)
969 continue;
970 fd = vg_waiting_fds[i].fd;
971 /* VG_(printf)("adding QUERY for fd %d\n", fd); */
sewardje462e202002-04-13 04:09:07 +0000972 vg_assert(fd >= 0);
sewardje663cb92002-04-12 10:26:32 +0000973 if (fd > fd_max)
974 fd_max = fd;
975 tid = vg_waiting_fds[i].tid;
sewardj6072c362002-04-19 14:40:57 +0000976 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +0000977 syscall_no = vg_waiting_fds[i].syscall_no;
978 switch (syscall_no) {
979 case __NR_read:
980 VKI_FD_SET(fd, &readfds); break;
981 case __NR_write:
982 VKI_FD_SET(fd, &writefds); break;
983 default:
984 VG_(panic)("poll_for_ready_fds: unexpected syscall");
985 /*NOTREACHED*/
986 break;
987 }
988 }
989
sewardje462e202002-04-13 04:09:07 +0000990 /* Short cut: if no fds are waiting, give up now. */
991 if (fd_max == -1)
992 return;
993
sewardje663cb92002-04-12 10:26:32 +0000994 /* BLOCK ALL SIGNALS. We don't want the complication of select()
995 getting interrupted. */
996 VG_(block_all_host_signals)( &saved_procmask );
997
998 n_ready = VG_(select)
999 ( fd_max+1, &readfds, &writefds, &exceptfds, &timeout);
1000 if (VG_(is_kerror)(n_ready)) {
1001 VG_(printf)("poll_for_ready_fds: select returned %d\n", n_ready);
1002 VG_(panic)("poll_for_ready_fds: select failed?!");
1003 /*NOTREACHED*/
1004 }
1005
1006 /* UNBLOCK ALL SIGNALS */
1007 VG_(restore_host_signals)( &saved_procmask );
1008
1009 /* VG_(printf)("poll_for_io_completions: %d fs ready\n", n_ready); */
1010
1011 if (n_ready == 0)
1012 return;
1013
1014 /* Inspect all the fds we know about, and handle any completions that
1015 have happened. */
1016 /*
1017 VG_(printf)("\n\n");
1018 for (fd = 0; fd < 100; fd++)
1019 if (VKI_FD_ISSET(fd, &writefds) || VKI_FD_ISSET(fd, &readfds)) {
1020 VG_(printf)("X"); } else { VG_(printf)("."); };
1021 VG_(printf)("\n\nfd_max = %d\n", fd_max);
1022 */
1023
1024 for (fd = 0; fd <= fd_max; fd++) {
1025 rd_ok = VKI_FD_ISSET(fd, &readfds);
1026 wr_ok = VKI_FD_ISSET(fd, &writefds);
1027 ex_ok = VKI_FD_ISSET(fd, &exceptfds);
1028
1029 n_ok = (rd_ok ? 1 : 0) + (wr_ok ? 1 : 0) + (ex_ok ? 1 : 0);
1030 if (n_ok == 0)
1031 continue;
1032 if (n_ok > 1) {
1033 VG_(printf)("offending fd = %d\n", fd);
1034 VG_(panic)("poll_for_ready_fds: multiple events on fd");
1035 }
1036
1037 /* An I/O event completed for fd. Find the thread which
1038 requested this. */
1039 for (i = 0; i < VG_N_WAITING_FDS; i++) {
1040 if (vg_waiting_fds[i].fd == -1 /* not in use */)
1041 continue;
1042 if (vg_waiting_fds[i].fd == fd)
1043 break;
1044 }
1045
1046 /* And a bit more paranoia ... */
1047 vg_assert(i >= 0 && i < VG_N_WAITING_FDS);
1048
1049 /* Mark the fd as ready. */
1050 vg_assert(! vg_waiting_fds[i].ready);
1051 vg_waiting_fds[i].ready = True;
1052 }
1053}
1054
1055
1056/* See comment attached to poll_for_ready_fds() for explaination. */
sewardj9a199dc2002-04-14 13:01:38 +00001057static
sewardje663cb92002-04-12 10:26:32 +00001058void complete_blocked_syscalls ( void )
1059{
1060 Int fd, i, res, syscall_no;
1061 ThreadId tid;
1062 Char msg_buf[100];
1063
1064 /* Inspect all the outstanding fds we know about. */
1065
1066 for (i = 0; i < VG_N_WAITING_FDS; i++) {
1067 if (vg_waiting_fds[i].fd == -1 /* not in use */)
1068 continue;
1069 if (! vg_waiting_fds[i].ready)
1070 continue;
1071
1072 fd = vg_waiting_fds[i].fd;
1073 tid = vg_waiting_fds[i].tid;
sewardj6072c362002-04-19 14:40:57 +00001074 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001075
1076 /* The thread actually has to be waiting for the I/O event it
1077 requested before we can deliver the result! */
1078 if (vg_threads[tid].status != VgTs_WaitFD)
1079 continue;
1080
1081 /* Ok, actually do it! We can safely use %EAX as the syscall
1082 number, because the speculative call made by
1083 sched_do_syscall() doesn't change %EAX in the case where the
1084 call would have blocked. */
1085
1086 syscall_no = vg_waiting_fds[i].syscall_no;
1087 vg_assert(syscall_no == vg_threads[tid].m_eax);
1088 KERNEL_DO_SYSCALL(tid,res);
1089 VG_(check_known_blocking_syscall)(tid, syscall_no, &res /* POST */);
1090
1091 /* Reschedule. */
1092 vg_threads[tid].status = VgTs_Runnable;
1093 /* Mark slot as no longer in use. */
1094 vg_waiting_fds[i].fd = -1;
1095 /* pp_sched_status(); */
sewardj8937c812002-04-12 20:12:20 +00001096 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001097 VG_(sprintf)(msg_buf,"resume due to I/O completion on fd %d", fd);
1098 print_sched_event(tid, msg_buf);
1099 }
1100 }
1101}
1102
1103
1104static
sewardj5f07b662002-04-23 16:52:51 +00001105void check_for_pthread_cond_timedwait ( void )
1106{
sewardj51c0aaf2002-04-25 01:32:10 +00001107 Int i, now;
sewardj5f07b662002-04-23 16:52:51 +00001108 for (i = 1; i < VG_N_THREADS; i++) {
1109 if (vg_threads[i].status != VgTs_WaitCV)
1110 continue;
1111 if (vg_threads[i].awaken_at == 0xFFFFFFFF /* no timeout */)
1112 continue;
sewardj51c0aaf2002-04-25 01:32:10 +00001113 now = VG_(read_millisecond_timer)();
1114 if (now >= vg_threads[i].awaken_at) {
sewardj5f07b662002-04-23 16:52:51 +00001115 do_pthread_cond_timedwait_TIMEOUT(i);
sewardj51c0aaf2002-04-25 01:32:10 +00001116 }
sewardj5f07b662002-04-23 16:52:51 +00001117 }
1118}
1119
1120
1121static
sewardje663cb92002-04-12 10:26:32 +00001122void nanosleep_for_a_while ( void )
1123{
1124 Int res;
1125 struct vki_timespec req;
1126 struct vki_timespec rem;
1127 req.tv_sec = 0;
sewardj51c0aaf2002-04-25 01:32:10 +00001128 req.tv_nsec = 20 * 1000 * 1000;
sewardje663cb92002-04-12 10:26:32 +00001129 res = VG_(nanosleep)( &req, &rem );
sewardj5f07b662002-04-23 16:52:51 +00001130 vg_assert(res == 0 /* ok */ || res == 1 /* interrupted by signal */);
sewardje663cb92002-04-12 10:26:32 +00001131}
1132
1133
1134/* ---------------------------------------------------------------------
1135 The scheduler proper.
1136 ------------------------------------------------------------------ */
1137
1138/* Run user-space threads until either
1139 * Deadlock occurs
1140 * One thread asks to shutdown Valgrind
1141 * The specified number of basic blocks has gone by.
1142*/
1143VgSchedReturnCode VG_(scheduler) ( void )
1144{
1145 ThreadId tid, tid_next;
1146 UInt trc;
1147 UInt dispatch_ctr_SAVED;
sewardj51c0aaf2002-04-25 01:32:10 +00001148 Int request_code, done_this_time, n_in_bounded_wait;
sewardje663cb92002-04-12 10:26:32 +00001149 Char msg_buf[100];
1150 Addr trans_addr;
sewardj14e03422002-04-24 19:51:31 +00001151 Bool sigs_delivered;
sewardje663cb92002-04-12 10:26:32 +00001152
1153 /* For the LRU structures, records when the epoch began. */
1154 ULong lru_epoch_started_at = 0;
1155
1156 /* Start with the root thread. tid in general indicates the
1157 currently runnable/just-finished-running thread. */
sewardj6072c362002-04-19 14:40:57 +00001158 tid = 1;
sewardje663cb92002-04-12 10:26:32 +00001159
1160 /* This is the top level scheduler loop. It falls into three
1161 phases. */
1162 while (True) {
1163
sewardj6072c362002-04-19 14:40:57 +00001164 /* ======================= Phase 0 of 3 =======================
1165 Be paranoid. Always a good idea. */
sewardjd7fd4d22002-04-24 01:57:27 +00001166 stage1:
sewardj6072c362002-04-19 14:40:57 +00001167 scheduler_sanity();
1168
sewardje663cb92002-04-12 10:26:32 +00001169 /* ======================= Phase 1 of 3 =======================
1170 Handle I/O completions and signals. This may change the
1171 status of various threads. Then select a new thread to run,
1172 or declare deadlock, or sleep if there are no runnable
1173 threads but some are blocked on I/O. */
1174
1175 /* Age the LRU structures if an epoch has been completed. */
1176 if (VG_(bbs_done) - lru_epoch_started_at >= VG_BBS_PER_EPOCH) {
1177 lru_epoch_started_at = VG_(bbs_done);
1178 increment_epoch();
1179 }
1180
1181 /* Was a debug-stop requested? */
1182 if (VG_(bbs_to_go) == 0)
1183 goto debug_stop;
1184
1185 /* Do the following loop until a runnable thread is found, or
1186 deadlock is detected. */
1187 while (True) {
1188
1189 /* For stats purposes only. */
1190 VG_(num_scheduling_events_MAJOR) ++;
1191
1192 /* See if any I/O operations which we were waiting for have
1193 completed, and, if so, make runnable the relevant waiting
1194 threads. */
1195 poll_for_ready_fds();
1196 complete_blocked_syscalls();
sewardj5f07b662002-04-23 16:52:51 +00001197 check_for_pthread_cond_timedwait();
sewardje663cb92002-04-12 10:26:32 +00001198
1199 /* See if there are any signals which need to be delivered. If
1200 so, choose thread(s) to deliver them to, and build signal
1201 delivery frames on those thread(s) stacks. */
sewardj6072c362002-04-19 14:40:57 +00001202
1203 /* Be careful about delivering signals to a thread waiting
1204 for a mutex. In particular, when the handler is running,
1205 that thread is temporarily apparently-not-waiting for the
1206 mutex, so if it is unlocked by another thread whilst the
1207 handler is running, this thread is not informed. When the
1208 handler returns, the thread resumes waiting on the mutex,
1209 even if, as a result, it has missed the unlocking of it.
1210 Potential deadlock. This sounds all very strange, but the
1211 POSIX standard appears to require this behaviour. */
sewardj14e03422002-04-24 19:51:31 +00001212 sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
1213 if (sigs_delivered)
1214 VG_(do_sanity_checks)( 1 /*HACK*/, False );
sewardje663cb92002-04-12 10:26:32 +00001215
1216 /* Try and find a thread (tid) to run. */
1217 tid_next = tid;
sewardj51c0aaf2002-04-25 01:32:10 +00001218 n_in_bounded_wait = 0;
sewardje663cb92002-04-12 10:26:32 +00001219 while (True) {
1220 tid_next++;
sewardj6072c362002-04-19 14:40:57 +00001221 if (tid_next >= VG_N_THREADS) tid_next = 1;
sewardj54cacf02002-04-12 23:24:59 +00001222 if (vg_threads[tid_next].status == VgTs_WaitFD
sewardj51c0aaf2002-04-25 01:32:10 +00001223 || vg_threads[tid_next].status == VgTs_Sleeping
1224 || (vg_threads[tid_next].status == VgTs_WaitCV
1225 && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
1226 n_in_bounded_wait ++;
sewardje663cb92002-04-12 10:26:32 +00001227 if (vg_threads[tid_next].status == VgTs_Runnable)
1228 break; /* We can run this one. */
1229 if (tid_next == tid)
1230 break; /* been all the way round */
1231 }
1232 tid = tid_next;
1233
1234 if (vg_threads[tid].status == VgTs_Runnable) {
1235 /* Found a suitable candidate. Fall out of this loop, so
1236 we can advance to stage 2 of the scheduler: actually
1237 running the thread. */
1238 break;
1239 }
1240
1241 /* We didn't find a runnable thread. Now what? */
sewardj51c0aaf2002-04-25 01:32:10 +00001242 if (n_in_bounded_wait == 0) {
sewardj54cacf02002-04-12 23:24:59 +00001243 /* No runnable threads and no prospect of any appearing
1244 even if we wait for an arbitrary length of time. In
1245 short, we have a deadlock. */
sewardj15a43e12002-04-17 19:35:12 +00001246 VG_(pp_sched_status)();
sewardje663cb92002-04-12 10:26:32 +00001247 return VgSrc_Deadlock;
1248 }
1249
1250 /* At least one thread is in a fd-wait state. Delay for a
1251 while, and go round again, in the hope that eventually a
1252 thread becomes runnable. */
1253 nanosleep_for_a_while();
1254 // pp_sched_status();
1255 // VG_(printf)(".\n");
1256 }
1257
1258
1259 /* ======================= Phase 2 of 3 =======================
1260 Wahey! We've finally decided that thread tid is runnable, so
1261 we now do that. Run it for as much of a quanta as possible.
1262 Trivial requests are handled and the thread continues. The
1263 aim is not to do too many of Phase 1 since it is expensive. */
1264
1265 if (0)
sewardj3b5d8862002-04-20 13:53:23 +00001266 VG_(printf)("SCHED: tid %d\n", tid);
sewardje663cb92002-04-12 10:26:32 +00001267
1268 /* Figure out how many bbs to ask vg_run_innerloop to do. Note
1269 that it decrements the counter before testing it for zero, so
1270 that if VG_(dispatch_ctr) is set to N you get at most N-1
1271 iterations. Also this means that VG_(dispatch_ctr) must
1272 exceed zero before entering the innerloop. Also also, the
1273 decrement is done before the bb is actually run, so you
1274 always get at least one decrement even if nothing happens.
1275 */
1276 if (VG_(bbs_to_go) >= VG_SCHEDULING_QUANTUM)
1277 VG_(dispatch_ctr) = VG_SCHEDULING_QUANTUM + 1;
1278 else
1279 VG_(dispatch_ctr) = (UInt)VG_(bbs_to_go) + 1;
1280
1281 /* ... and remember what we asked for. */
1282 dispatch_ctr_SAVED = VG_(dispatch_ctr);
1283
sewardj1e8cdc92002-04-18 11:37:52 +00001284 /* paranoia ... */
1285 vg_assert(vg_threads[tid].tid == tid);
1286
sewardje663cb92002-04-12 10:26:32 +00001287 /* Actually run thread tid. */
1288 while (True) {
1289
1290 /* For stats purposes only. */
1291 VG_(num_scheduling_events_MINOR) ++;
1292
1293 if (0)
1294 VG_(message)(Vg_DebugMsg, "thread %d: running for %d bbs",
1295 tid, VG_(dispatch_ctr) - 1 );
1296
1297 trc = run_thread_for_a_while ( tid );
1298
1299 /* Deal quickly with trivial scheduling events, and resume the
1300 thread. */
1301
1302 if (trc == VG_TRC_INNER_FASTMISS) {
1303 vg_assert(VG_(dispatch_ctr) > 0);
1304
1305 /* Trivial event. Miss in the fast-cache. Do a full
1306 lookup for it. */
1307 trans_addr
1308 = VG_(search_transtab) ( vg_threads[tid].m_eip );
1309 if (trans_addr == (Addr)0) {
1310 /* Not found; we need to request a translation. */
sewardj1e8cdc92002-04-18 11:37:52 +00001311 create_translation_for( tid, vg_threads[tid].m_eip );
sewardje663cb92002-04-12 10:26:32 +00001312 trans_addr = VG_(search_transtab) ( vg_threads[tid].m_eip );
1313 if (trans_addr == (Addr)0)
1314 VG_(panic)("VG_TRC_INNER_FASTMISS: missing tt_fast entry");
1315 }
1316 continue; /* with this thread */
1317 }
1318
1319 if (trc == VG_TRC_EBP_JMP_CLIENTREQ) {
sewardjd7fd4d22002-04-24 01:57:27 +00001320 Bool done = maybe_do_trivial_clientreq(tid);
1321 if (done) {
1322 /* The request is done. We try and continue with the
1323 same thread if still runnable. If not, go back to
1324 Stage 1 to select a new thread to run. */
1325 if (vg_threads[tid].status == VgTs_Runnable)
1326 continue; /* with this thread */
1327 else
1328 goto stage1;
sewardje663cb92002-04-12 10:26:32 +00001329 }
1330 }
1331
sewardj51c0aaf2002-04-25 01:32:10 +00001332 if (trc == VG_TRC_EBP_JMP_SYSCALL) {
1333 /* Do a syscall for the vthread tid. This could cause it
1334 to become non-runnable. */
1335 sched_do_syscall(tid);
1336 if (vg_threads[tid].status == VgTs_Runnable)
1337 continue; /* with this thread */
1338 else
1339 goto stage1;
1340 }
1341
sewardjd7fd4d22002-04-24 01:57:27 +00001342 /* It's an event we can't quickly deal with. Give up running
1343 this thread and handle things the expensive way. */
sewardje663cb92002-04-12 10:26:32 +00001344 break;
1345 }
1346
1347 /* ======================= Phase 3 of 3 =======================
1348 Handle non-trivial thread requests, mostly pthread stuff. */
1349
1350 /* Ok, we've fallen out of the dispatcher for a
1351 non-completely-trivial reason. First, update basic-block
1352 counters. */
1353
1354 done_this_time = (Int)dispatch_ctr_SAVED - (Int)VG_(dispatch_ctr) - 1;
1355 vg_assert(done_this_time >= 0);
1356 VG_(bbs_to_go) -= (ULong)done_this_time;
1357 VG_(bbs_done) += (ULong)done_this_time;
1358
1359 if (0 && trc != VG_TRC_INNER_FASTMISS)
1360 VG_(message)(Vg_DebugMsg, "thread %d: completed %d bbs, trc %d",
1361 tid, done_this_time, (Int)trc );
1362
1363 if (0 && trc != VG_TRC_INNER_FASTMISS)
1364 VG_(message)(Vg_DebugMsg, "thread %d: %ld bbs, event %s",
1365 tid, VG_(bbs_done),
1366 name_of_sched_event(trc) );
sewardj9d1b5d32002-04-17 19:40:49 +00001367
sewardje663cb92002-04-12 10:26:32 +00001368 /* Examine the thread's return code to figure out why it
1369 stopped, and handle requests. */
1370
1371 switch (trc) {
1372
1373 case VG_TRC_INNER_FASTMISS:
1374 VG_(panic)("VG_(scheduler): VG_TRC_INNER_FASTMISS");
1375 /*NOTREACHED*/
1376 break;
1377
1378 case VG_TRC_INNER_COUNTERZERO:
1379 /* Timeslice is out. Let a new thread be scheduled,
1380 simply by doing nothing, causing us to arrive back at
1381 Phase 1. */
1382 if (VG_(bbs_to_go) == 0) {
1383 goto debug_stop;
1384 }
1385 vg_assert(VG_(dispatch_ctr) == 0);
1386 break;
1387
1388 case VG_TRC_UNRESUMABLE_SIGNAL:
1389 /* It got a SIGSEGV/SIGBUS, which we need to deliver right
1390 away. Again, do nothing, so we wind up back at Phase
1391 1, whereupon the signal will be "delivered". */
1392 break;
1393
sewardj51c0aaf2002-04-25 01:32:10 +00001394#if 0
sewardje663cb92002-04-12 10:26:32 +00001395 case VG_TRC_EBP_JMP_SYSCALL:
1396 /* Do a syscall for the vthread tid. This could cause it
1397 to become non-runnable. */
1398 sched_do_syscall(tid);
1399 break;
sewardj51c0aaf2002-04-25 01:32:10 +00001400#endif
sewardje663cb92002-04-12 10:26:32 +00001401
1402 case VG_TRC_EBP_JMP_CLIENTREQ:
1403 /* Do a client request for the vthread tid. Note that
1404 some requests will have been handled by
1405 maybe_do_trivial_clientreq(), so we don't expect to see
1406 those here.
1407 */
sewardj54cacf02002-04-12 23:24:59 +00001408 /* The thread's %EAX points at an arg block, the first
1409 word of which is the request code. */
1410 request_code = ((UInt*)(vg_threads[tid].m_eax))[0];
sewardje663cb92002-04-12 10:26:32 +00001411 if (0) {
sewardj54cacf02002-04-12 23:24:59 +00001412 VG_(sprintf)(msg_buf, "request 0x%x", request_code );
sewardje663cb92002-04-12 10:26:32 +00001413 print_sched_event(tid, msg_buf);
1414 }
1415 /* Do a non-trivial client request for thread tid. tid's
1416 %EAX points to a short vector of argument words, the
1417 first of which is the request code. The result of the
1418 request is put in tid's %EDX. Alternatively, perhaps
1419 the request causes tid to become non-runnable and/or
1420 other blocked threads become runnable. In general we
1421 can and often do mess with the state of arbitrary
1422 threads at this point. */
sewardj54cacf02002-04-12 23:24:59 +00001423 if (request_code == VG_USERREQ__SHUTDOWN_VALGRIND) {
1424 return VgSrc_Shutdown;
1425 } else {
1426 do_nontrivial_clientreq(tid);
1427 }
sewardje663cb92002-04-12 10:26:32 +00001428 break;
1429
1430 default:
1431 VG_(printf)("\ntrc = %d\n", trc);
1432 VG_(panic)("VG_(scheduler), phase 3: "
1433 "unexpected thread return code");
1434 /* NOTREACHED */
1435 break;
1436
1437 } /* switch (trc) */
1438
1439 /* That completes Phase 3 of 3. Return now to the top of the
1440 main scheduler loop, to Phase 1 of 3. */
1441
1442 } /* top-level scheduler loop */
1443
1444
1445 /* NOTREACHED */
1446 VG_(panic)("scheduler: post-main-loop ?!");
1447 /* NOTREACHED */
1448
1449 debug_stop:
1450 /* If we exited because of a debug stop, print the translation
1451 of the last block executed -- by translating it again, and
1452 throwing away the result. */
1453 VG_(printf)(
1454 "======vvvvvvvv====== LAST TRANSLATION ======vvvvvvvv======\n");
sewardj1e8cdc92002-04-18 11:37:52 +00001455 VG_(translate)( &vg_threads[tid], vg_threads[tid].m_eip, NULL, NULL, NULL );
sewardje663cb92002-04-12 10:26:32 +00001456 VG_(printf)("\n");
1457 VG_(printf)(
1458 "======^^^^^^^^====== LAST TRANSLATION ======^^^^^^^^======\n");
1459
1460 return VgSrc_BbsDone;
1461}
1462
1463
1464/* ---------------------------------------------------------------------
1465 The pthread implementation.
1466 ------------------------------------------------------------------ */
1467
1468#include <pthread.h>
1469#include <errno.h>
1470
1471#if !defined(PTHREAD_STACK_MIN)
1472# define PTHREAD_STACK_MIN (16384 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB)
1473#endif
1474
1475/* /usr/include/bits/pthreadtypes.h:
1476 typedef unsigned long int pthread_t;
1477*/
1478
sewardje663cb92002-04-12 10:26:32 +00001479
sewardj604ec3c2002-04-18 22:38:41 +00001480/* -----------------------------------------------------------
1481 Thread CREATION, JOINAGE and CANCELLATION.
1482 -------------------------------------------------------- */
1483
sewardje663cb92002-04-12 10:26:32 +00001484static
sewardj853f55d2002-04-26 00:27:53 +00001485void do_pthread_cancel ( ThreadId tid,
sewardje663cb92002-04-12 10:26:32 +00001486 pthread_t tid_cancellee )
1487{
1488 Char msg_buf[100];
sewardj853f55d2002-04-26 00:27:53 +00001489
1490 vg_assert(is_valid_tid(tid));
1491 vg_assert(vg_threads[tid].status != VgTs_Empty);
1492
1493 if (!is_valid_tid(tid_cancellee)
1494 || vg_threads[tid_cancellee].status == VgTs_Empty) {
1495 vg_threads[tid].m_edx = ESRCH;
1496 return;
1497 }
1498
sewardje663cb92002-04-12 10:26:32 +00001499 /* We want make is appear that this thread has returned to
1500 do_pthread_create_bogusRA with PTHREAD_CANCELED as the
1501 return value. So: simple: put PTHREAD_CANCELED into %EAX
1502 and &do_pthread_create_bogusRA into %EIP and keep going! */
sewardj8937c812002-04-12 20:12:20 +00001503 if (VG_(clo_trace_sched)) {
sewardj853f55d2002-04-26 00:27:53 +00001504 VG_(sprintf)(msg_buf, "cancelled by %d", tid);
sewardje663cb92002-04-12 10:26:32 +00001505 print_sched_event(tid_cancellee, msg_buf);
1506 }
1507 vg_threads[tid_cancellee].m_eax = (UInt)PTHREAD_CANCELED;
sewardjbc5b99f2002-04-13 00:08:51 +00001508 vg_threads[tid_cancellee].m_eip = (UInt)&VG_(pthreadreturn_bogusRA);
sewardje663cb92002-04-12 10:26:32 +00001509 vg_threads[tid_cancellee].status = VgTs_Runnable;
sewardj853f55d2002-04-26 00:27:53 +00001510
1511 /* We return with success (0). */
1512 vg_threads[tid].m_edx = 0;
sewardje663cb92002-04-12 10:26:32 +00001513}
1514
1515
sewardj3b5d8862002-04-20 13:53:23 +00001516static
1517void do_pthread_exit ( ThreadId tid, void* retval )
1518{
1519 Char msg_buf[100];
1520 /* We want make is appear that this thread has returned to
1521 do_pthread_create_bogusRA with retval as the
1522 return value. So: simple: put retval into %EAX
1523 and &do_pthread_create_bogusRA into %EIP and keep going! */
1524 if (VG_(clo_trace_sched)) {
1525 VG_(sprintf)(msg_buf, "exiting with %p", retval);
1526 print_sched_event(tid, msg_buf);
1527 }
1528 vg_threads[tid].m_eax = (UInt)retval;
1529 vg_threads[tid].m_eip = (UInt)&VG_(pthreadreturn_bogusRA);
1530 vg_threads[tid].status = VgTs_Runnable;
1531}
1532
sewardje663cb92002-04-12 10:26:32 +00001533
1534/* Thread tid is exiting, by returning from the function it was
sewardjbc5b99f2002-04-13 00:08:51 +00001535 created with. Or possibly due to pthread_exit or cancellation.
1536 The main complication here is to resume any thread waiting to join
1537 with this one. */
sewardje663cb92002-04-12 10:26:32 +00001538static
sewardjbc5b99f2002-04-13 00:08:51 +00001539void handle_pthread_return ( ThreadId tid, void* retval )
sewardje663cb92002-04-12 10:26:32 +00001540{
1541 ThreadId jnr; /* joiner, the thread calling pthread_join. */
1542 UInt* jnr_args;
1543 void** jnr_thread_return;
1544 Char msg_buf[100];
1545
1546 /* Mark it as not in use. Leave the stack in place so the next
1547 user of this slot doesn't reallocate it. */
sewardj6072c362002-04-19 14:40:57 +00001548 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001549 vg_assert(vg_threads[tid].status != VgTs_Empty);
1550
sewardjbc5b99f2002-04-13 00:08:51 +00001551 vg_threads[tid].retval = retval;
sewardje663cb92002-04-12 10:26:32 +00001552
1553 if (vg_threads[tid].joiner == VG_INVALID_THREADID) {
1554 /* No one has yet done a join on me */
1555 vg_threads[tid].status = VgTs_WaitJoiner;
sewardj8937c812002-04-12 20:12:20 +00001556 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001557 VG_(sprintf)(msg_buf,
1558 "root fn returns, waiting for a call pthread_join(%d)",
1559 tid);
1560 print_sched_event(tid, msg_buf);
1561 }
1562 } else {
1563 /* Some is waiting; make their join call return with success,
1564 putting my exit code in the place specified by the caller's
1565 thread_return param. This is all very horrible, since we
1566 need to consult the joiner's arg block -- pointed to by its
1567 %EAX -- in order to extract the 2nd param of its pthread_join
1568 call. TODO: free properly the slot (also below).
1569 */
1570 jnr = vg_threads[tid].joiner;
sewardj6072c362002-04-19 14:40:57 +00001571 vg_assert(is_valid_tid(jnr));
sewardje663cb92002-04-12 10:26:32 +00001572 vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
1573 jnr_args = (UInt*)vg_threads[jnr].m_eax;
1574 jnr_thread_return = (void**)(jnr_args[2]);
1575 if (jnr_thread_return != NULL)
1576 *jnr_thread_return = vg_threads[tid].retval;
1577 vg_threads[jnr].m_edx = 0; /* success */
1578 vg_threads[jnr].status = VgTs_Runnable;
1579 vg_threads[tid].status = VgTs_Empty; /* bye! */
sewardj75fe1892002-04-14 02:46:33 +00001580 if (VG_(clo_instrument) && tid != 0)
1581 VGM_(make_noaccess)( vg_threads[tid].stack_base,
1582 vg_threads[tid].stack_size );
sewardj8937c812002-04-12 20:12:20 +00001583 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001584 VG_(sprintf)(msg_buf,
1585 "root fn returns, to find a waiting pthread_join(%d)", tid);
1586 print_sched_event(tid, msg_buf);
1587 VG_(sprintf)(msg_buf,
1588 "my pthread_join(%d) returned; resuming", tid);
1589 print_sched_event(jnr, msg_buf);
1590 }
1591 }
1592
1593 /* Return value is irrelevant; this thread will not get
1594 rescheduled. */
1595}
1596
1597
1598static
1599void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
1600{
1601 Char msg_buf[100];
1602
1603 /* jee, the joinee, is the thread specified as an arg in thread
1604 tid's call to pthread_join. So tid is the join-er. */
sewardj6072c362002-04-19 14:40:57 +00001605 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001606 vg_assert(vg_threads[tid].status == VgTs_Runnable);
1607
1608 if (jee == tid) {
1609 vg_threads[tid].m_edx = EDEADLK; /* libc constant, not a kernel one */
1610 vg_threads[tid].status = VgTs_Runnable;
1611 return;
1612 }
1613
1614 if (jee < 0
1615 || jee >= VG_N_THREADS
1616 || vg_threads[jee].status == VgTs_Empty) {
1617 /* Invalid thread to join to. */
1618 vg_threads[tid].m_edx = EINVAL;
1619 vg_threads[tid].status = VgTs_Runnable;
1620 return;
1621 }
1622
1623 if (vg_threads[jee].joiner != VG_INVALID_THREADID) {
1624 /* Someone already did join on this thread */
1625 vg_threads[tid].m_edx = EINVAL;
1626 vg_threads[tid].status = VgTs_Runnable;
1627 return;
1628 }
1629
1630 /* if (vg_threads[jee].detached) ... */
1631
1632 /* Perhaps the joinee has already finished? If so return
1633 immediately with its return code, and free up the slot. TODO:
1634 free it properly (also above). */
1635 if (vg_threads[jee].status == VgTs_WaitJoiner) {
1636 vg_assert(vg_threads[jee].joiner == VG_INVALID_THREADID);
1637 vg_threads[tid].m_edx = 0; /* success */
1638 if (thread_return != NULL)
1639 *thread_return = vg_threads[jee].retval;
1640 vg_threads[tid].status = VgTs_Runnable;
1641 vg_threads[jee].status = VgTs_Empty; /* bye! */
sewardj75fe1892002-04-14 02:46:33 +00001642 if (VG_(clo_instrument) && jee != 0)
1643 VGM_(make_noaccess)( vg_threads[jee].stack_base,
1644 vg_threads[jee].stack_size );
sewardj8937c812002-04-12 20:12:20 +00001645 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001646 VG_(sprintf)(msg_buf,
1647 "someone called pthread_join() on me; bye!");
1648 print_sched_event(jee, msg_buf);
1649 VG_(sprintf)(msg_buf,
1650 "my pthread_join(%d) returned immediately",
1651 jee );
1652 print_sched_event(tid, msg_buf);
1653 }
1654 return;
1655 }
1656
1657 /* Ok, so we'll have to wait on jee. */
1658 vg_threads[jee].joiner = tid;
1659 vg_threads[tid].status = VgTs_WaitJoinee;
sewardj8937c812002-04-12 20:12:20 +00001660 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001661 VG_(sprintf)(msg_buf,
1662 "blocking on call of pthread_join(%d)", jee );
1663 print_sched_event(tid, msg_buf);
1664 }
1665 /* So tid's join call does not return just now. */
1666}
1667
1668
1669static
1670void do_pthread_create ( ThreadId parent_tid,
1671 pthread_t* thread,
1672 pthread_attr_t* attr,
1673 void* (*start_routine)(void *),
1674 void* arg )
1675{
sewardj5f07b662002-04-23 16:52:51 +00001676 Int i;
sewardje663cb92002-04-12 10:26:32 +00001677 Addr new_stack;
1678 UInt new_stk_szb;
1679 ThreadId tid;
1680 Char msg_buf[100];
1681
1682 /* Paranoia ... */
1683 vg_assert(sizeof(pthread_t) == sizeof(UInt));
1684
1685 vg_assert(vg_threads[parent_tid].status != VgTs_Empty);
1686
sewardj1e8cdc92002-04-18 11:37:52 +00001687 tid = vg_alloc_ThreadState();
sewardje663cb92002-04-12 10:26:32 +00001688
1689 /* If we've created the main thread's tid, we're in deep trouble :) */
sewardj6072c362002-04-19 14:40:57 +00001690 vg_assert(tid != 1);
1691 vg_assert(is_valid_tid(tid));
sewardje663cb92002-04-12 10:26:32 +00001692
1693 /* Copy the parent's CPU state into the child's, in a roundabout
1694 way (via baseBlock). */
1695 VG_(load_thread_state)(parent_tid);
1696 VG_(save_thread_state)(tid);
1697
1698 /* Consider allocating the child a stack, if the one it already has
1699 is inadequate. */
1700 new_stk_szb = PTHREAD_STACK_MIN;
1701
1702 if (new_stk_szb > vg_threads[tid].stack_size) {
1703 /* Again, for good measure :) We definitely don't want to be
1704 allocating a stack for the main thread. */
sewardj6072c362002-04-19 14:40:57 +00001705 vg_assert(tid != 1);
sewardje663cb92002-04-12 10:26:32 +00001706 /* for now, we don't handle the case of anything other than
1707 assigning it for the first time. */
1708 vg_assert(vg_threads[tid].stack_size == 0);
1709 vg_assert(vg_threads[tid].stack_base == (Addr)NULL);
1710 new_stack = (Addr)VG_(get_memory_from_mmap)( new_stk_szb );
1711 vg_threads[tid].stack_base = new_stack;
1712 vg_threads[tid].stack_size = new_stk_szb;
sewardj1e8cdc92002-04-18 11:37:52 +00001713 vg_threads[tid].stack_highest_word
sewardje663cb92002-04-12 10:26:32 +00001714 = new_stack + new_stk_szb
sewardj1e8cdc92002-04-18 11:37:52 +00001715 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; /* -4 ??? */;
sewardje663cb92002-04-12 10:26:32 +00001716 }
sewardj1e8cdc92002-04-18 11:37:52 +00001717
1718 vg_threads[tid].m_esp
1719 = vg_threads[tid].stack_base
1720 + vg_threads[tid].stack_size
1721 - VG_AR_CLIENT_STACKBASE_REDZONE_SZB;
1722
sewardje663cb92002-04-12 10:26:32 +00001723 if (VG_(clo_instrument))
1724 VGM_(make_noaccess)( vg_threads[tid].m_esp,
1725 VG_AR_CLIENT_STACKBASE_REDZONE_SZB );
1726
1727 /* push arg */
1728 vg_threads[tid].m_esp -= 4;
1729 * (UInt*)(vg_threads[tid].m_esp) = (UInt)arg;
1730
1731 /* push (magical) return address */
1732 vg_threads[tid].m_esp -= 4;
sewardjbc5b99f2002-04-13 00:08:51 +00001733 * (UInt*)(vg_threads[tid].m_esp) = (UInt)VG_(pthreadreturn_bogusRA);
sewardje663cb92002-04-12 10:26:32 +00001734
1735 if (VG_(clo_instrument))
1736 VGM_(make_readable)( vg_threads[tid].m_esp, 2 * 4 );
1737
1738 /* this is where we start */
1739 vg_threads[tid].m_eip = (UInt)start_routine;
1740
sewardj8937c812002-04-12 20:12:20 +00001741 if (VG_(clo_trace_sched)) {
sewardje663cb92002-04-12 10:26:32 +00001742 VG_(sprintf)(msg_buf,
1743 "new thread, created by %d", parent_tid );
1744 print_sched_event(tid, msg_buf);
1745 }
1746
1747 /* store the thread id in *thread. */
1748 // if (VG_(clo_instrument))
1749 // ***** CHECK *thread is writable
1750 *thread = (pthread_t)tid;
1751
sewardj3b5d8862002-04-20 13:53:23 +00001752 vg_threads[tid].associated_mx = NULL;
1753 vg_threads[tid].associated_cv = NULL;
1754 vg_threads[tid].joiner = VG_INVALID_THREADID;
1755 vg_threads[tid].status = VgTs_Runnable;
sewardj604ec3c2002-04-18 22:38:41 +00001756
sewardj5f07b662002-04-23 16:52:51 +00001757 for (i = 0; i < VG_N_THREAD_KEYS; i++)
1758 vg_threads[tid].specifics[i] = NULL;
1759
sewardj604ec3c2002-04-18 22:38:41 +00001760 /* return zero */
sewardje663cb92002-04-12 10:26:32 +00001761 vg_threads[tid].m_edx = 0; /* success */
1762}
1763
1764
sewardj604ec3c2002-04-18 22:38:41 +00001765/* -----------------------------------------------------------
1766 MUTEXes
1767 -------------------------------------------------------- */
1768
sewardj604ec3c2002-04-18 22:38:41 +00001769/* pthread_mutex_t is a struct with at 5 words:
sewardje663cb92002-04-12 10:26:32 +00001770 typedef struct
1771 {
1772 int __m_reserved; -- Reserved for future use
1773 int __m_count; -- Depth of recursive locking
1774 _pthread_descr __m_owner; -- Owner thread (if recursive or errcheck)
1775 int __m_kind; -- Mutex kind: fast, recursive or errcheck
1776 struct _pthread_fastlock __m_lock; -- Underlying fast lock
1777 } pthread_mutex_t;
sewardj604ec3c2002-04-18 22:38:41 +00001778
sewardj6072c362002-04-19 14:40:57 +00001779 #define PTHREAD_MUTEX_INITIALIZER \
1780 {0, 0, 0, PTHREAD_MUTEX_TIMED_NP, __LOCK_INITIALIZER}
1781 # define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
1782 {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER}
1783 # define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \
1784 {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, __LOCK_INITIALIZER}
1785 # define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \
1786 {0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, __LOCK_INITIALIZER}
sewardj604ec3c2002-04-18 22:38:41 +00001787
sewardj6072c362002-04-19 14:40:57 +00001788 How we use it:
sewardj604ec3c2002-04-18 22:38:41 +00001789
sewardj6072c362002-04-19 14:40:57 +00001790 __m_kind never changes and indicates whether or not it is recursive.
1791
1792 __m_count indicates the lock count; if 0, the mutex is not owned by
1793 anybody.
1794
1795 __m_owner has a ThreadId value stuffed into it. We carefully arrange
1796 that ThreadId == 0 is invalid (VG_INVALID_THREADID), so that
1797 statically initialised mutexes correctly appear
1798 to belong to nobody.
1799
1800 In summary, a not-in-use mutex is distinguised by having __m_owner
1801 == 0 (VG_INVALID_THREADID) and __m_count == 0 too. If one of those
1802 conditions holds, the other should too.
1803
1804 There is no linked list of threads waiting for this mutex. Instead
1805 a thread in WaitMX state points at the mutex with its waited_on_mx
1806 field. This makes _unlock() inefficient, but simple to implement the
1807 right semantics viz-a-viz signals.
sewardje663cb92002-04-12 10:26:32 +00001808
sewardj604ec3c2002-04-18 22:38:41 +00001809 We don't have to deal with mutex initialisation; the client side
sewardj6072c362002-04-19 14:40:57 +00001810 deals with that for us.
1811*/
sewardje663cb92002-04-12 10:26:32 +00001812
sewardj3b5d8862002-04-20 13:53:23 +00001813/* Helper fns ... */
1814static
1815void release_one_thread_waiting_on_mutex ( pthread_mutex_t* mutex,
1816 Char* caller )
1817{
1818 Int i;
1819 Char msg_buf[100];
1820
1821 /* Find some arbitrary thread waiting on this mutex, and make it
1822 runnable. If none are waiting, mark the mutex as not held. */
1823 for (i = 1; i < VG_N_THREADS; i++) {
1824 if (vg_threads[i].status == VgTs_Empty)
1825 continue;
1826 if (vg_threads[i].status == VgTs_WaitMX
1827 && vg_threads[i].associated_mx == mutex)
1828 break;
1829 }
1830
1831 vg_assert(i <= VG_N_THREADS);
1832 if (i == VG_N_THREADS) {
1833 /* Nobody else is waiting on it. */
1834 mutex->__m_count = 0;
1835 mutex->__m_owner = VG_INVALID_THREADID;
1836 } else {
1837 /* Notionally transfer the hold to thread i, whose
1838 pthread_mutex_lock() call now returns with 0 (success). */
1839 /* The .count is already == 1. */
1840 vg_assert(vg_threads[i].associated_mx == mutex);
1841 mutex->__m_owner = (_pthread_descr)i;
1842 vg_threads[i].status = VgTs_Runnable;
1843 vg_threads[i].associated_mx = NULL;
sewardj5f07b662002-04-23 16:52:51 +00001844 /* m_edx already holds pth_mx_lock() success (0) */
sewardj3b5d8862002-04-20 13:53:23 +00001845
1846 if (VG_(clo_trace_pthread_level) >= 1) {
1847 VG_(sprintf)(msg_buf, "%s mx %p: RESUME",
1848 caller, mutex );
1849 print_pthread_event(i, msg_buf);
1850 }
1851 }
1852}
1853
sewardje663cb92002-04-12 10:26:32 +00001854
1855static
sewardj30671ff2002-04-21 00:13:57 +00001856void do_pthread_mutex_lock( ThreadId tid,
1857 Bool is_trylock,
sewardjd7fd4d22002-04-24 01:57:27 +00001858 void* /* pthread_mutex_t* */ mutexV )
sewardje663cb92002-04-12 10:26:32 +00001859{
sewardj30671ff2002-04-21 00:13:57 +00001860 Char msg_buf[100];
1861 Char* caller
1862 = is_trylock ? "pthread_mutex_lock "
1863 : "pthread_mutex_trylock";
sewardje663cb92002-04-12 10:26:32 +00001864
sewardjd7fd4d22002-04-24 01:57:27 +00001865 pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
1866
sewardj604ec3c2002-04-18 22:38:41 +00001867 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj30671ff2002-04-21 00:13:57 +00001868 VG_(sprintf)(msg_buf, "%s mx %p ...", caller, mutex );
sewardj604ec3c2002-04-18 22:38:41 +00001869 print_pthread_event(tid, msg_buf);
1870 }
1871
1872 /* Paranoia ... */
1873 vg_assert(is_valid_tid(tid)
1874 && vg_threads[tid].status == VgTs_Runnable);
sewardje663cb92002-04-12 10:26:32 +00001875
1876 /* POSIX doesn't mandate this, but for sanity ... */
1877 if (mutex == NULL) {
1878 vg_threads[tid].m_edx = EINVAL;
1879 return;
1880 }
1881
sewardj604ec3c2002-04-18 22:38:41 +00001882 /* More paranoia ... */
1883 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00001884# ifndef GLIBC_2_1
sewardj604ec3c2002-04-18 22:38:41 +00001885 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00001886 case PTHREAD_MUTEX_ADAPTIVE_NP:
1887# endif
sewardj604ec3c2002-04-18 22:38:41 +00001888 case PTHREAD_MUTEX_RECURSIVE_NP:
1889 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj604ec3c2002-04-18 22:38:41 +00001890 if (mutex->__m_count >= 0) break;
1891 /* else fall thru */
1892 default:
1893 vg_threads[tid].m_edx = EINVAL;
1894 return;
sewardje663cb92002-04-12 10:26:32 +00001895 }
1896
sewardj604ec3c2002-04-18 22:38:41 +00001897 if (mutex->__m_count > 0) {
sewardje663cb92002-04-12 10:26:32 +00001898
sewardj604ec3c2002-04-18 22:38:41 +00001899 vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
sewardjf8f819e2002-04-17 23:21:37 +00001900
1901 /* Someone has it already. */
sewardj604ec3c2002-04-18 22:38:41 +00001902 if ((ThreadId)mutex->__m_owner == tid) {
sewardjf8f819e2002-04-17 23:21:37 +00001903 /* It's locked -- by me! */
sewardj604ec3c2002-04-18 22:38:41 +00001904 if (mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP) {
sewardjf8f819e2002-04-17 23:21:37 +00001905 /* return 0 (success). */
sewardj604ec3c2002-04-18 22:38:41 +00001906 mutex->__m_count++;
sewardjf8f819e2002-04-17 23:21:37 +00001907 vg_threads[tid].m_edx = 0;
sewardj853f55d2002-04-26 00:27:53 +00001908 if (0)
1909 VG_(printf)("!!!!!! tid %d, mx %p -> locked %d\n",
1910 tid, mutex, mutex->__m_count);
sewardjf8f819e2002-04-17 23:21:37 +00001911 return;
1912 } else {
sewardj30671ff2002-04-21 00:13:57 +00001913 if (is_trylock)
1914 vg_threads[tid].m_edx = EBUSY;
1915 else
1916 vg_threads[tid].m_edx = EDEADLK;
sewardjf8f819e2002-04-17 23:21:37 +00001917 return;
1918 }
1919 } else {
sewardj6072c362002-04-19 14:40:57 +00001920 /* Someone else has it; we have to wait. Mark ourselves
1921 thusly. */
sewardj05553872002-04-20 20:53:17 +00001922 /* GUARD: __m_count > 0 && __m_owner is valid */
sewardj30671ff2002-04-21 00:13:57 +00001923 if (is_trylock) {
1924 /* caller is polling; so return immediately. */
1925 vg_threads[tid].m_edx = EBUSY;
1926 } else {
1927 vg_threads[tid].status = VgTs_WaitMX;
1928 vg_threads[tid].associated_mx = mutex;
sewardj5f07b662002-04-23 16:52:51 +00001929 vg_threads[tid].m_edx = 0; /* pth_mx_lock success value */
sewardj30671ff2002-04-21 00:13:57 +00001930 if (VG_(clo_trace_pthread_level) >= 1) {
1931 VG_(sprintf)(msg_buf, "%s mx %p: BLOCK",
1932 caller, mutex );
1933 print_pthread_event(tid, msg_buf);
1934 }
1935 }
sewardje663cb92002-04-12 10:26:32 +00001936 return;
1937 }
sewardjf8f819e2002-04-17 23:21:37 +00001938
sewardje663cb92002-04-12 10:26:32 +00001939 } else {
sewardj6072c362002-04-19 14:40:57 +00001940 /* Nobody owns it. Sanity check ... */
1941 vg_assert(mutex->__m_owner == VG_INVALID_THREADID);
sewardjf8f819e2002-04-17 23:21:37 +00001942 /* We get it! [for the first time]. */
sewardj604ec3c2002-04-18 22:38:41 +00001943 mutex->__m_count = 1;
1944 mutex->__m_owner = (_pthread_descr)tid;
sewardj3b5d8862002-04-20 13:53:23 +00001945 vg_assert(vg_threads[tid].associated_mx == NULL);
sewardje663cb92002-04-12 10:26:32 +00001946 /* return 0 (success). */
1947 vg_threads[tid].m_edx = 0;
1948 }
sewardjf8f819e2002-04-17 23:21:37 +00001949
sewardje663cb92002-04-12 10:26:32 +00001950}
1951
1952
1953static
1954void do_pthread_mutex_unlock ( ThreadId tid,
sewardjd7fd4d22002-04-24 01:57:27 +00001955 void* /* pthread_mutex_t* */ mutexV )
sewardje663cb92002-04-12 10:26:32 +00001956{
sewardj3b5d8862002-04-20 13:53:23 +00001957 Char msg_buf[100];
sewardjd7fd4d22002-04-24 01:57:27 +00001958 pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
sewardje663cb92002-04-12 10:26:32 +00001959
sewardj45b4b372002-04-16 22:50:32 +00001960 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj3b5d8862002-04-20 13:53:23 +00001961 VG_(sprintf)(msg_buf, "pthread_mutex_unlock mx %p ...", mutex );
sewardj8937c812002-04-12 20:12:20 +00001962 print_pthread_event(tid, msg_buf);
1963 }
1964
sewardj604ec3c2002-04-18 22:38:41 +00001965 /* Paranoia ... */
1966 vg_assert(is_valid_tid(tid)
1967 && vg_threads[tid].status == VgTs_Runnable);
1968
1969 if (mutex == NULL) {
1970 vg_threads[tid].m_edx = EINVAL;
1971 return;
1972 }
1973
1974 /* More paranoia ... */
1975 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00001976# ifndef GLIBC_2_1
sewardj604ec3c2002-04-18 22:38:41 +00001977 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00001978 case PTHREAD_MUTEX_ADAPTIVE_NP:
1979# endif
sewardj604ec3c2002-04-18 22:38:41 +00001980 case PTHREAD_MUTEX_RECURSIVE_NP:
1981 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj604ec3c2002-04-18 22:38:41 +00001982 if (mutex->__m_count >= 0) break;
1983 /* else fall thru */
1984 default:
1985 vg_threads[tid].m_edx = EINVAL;
1986 return;
1987 }
sewardje663cb92002-04-12 10:26:32 +00001988
1989 /* Barf if we don't currently hold the mutex. */
sewardj604ec3c2002-04-18 22:38:41 +00001990 if (mutex->__m_count == 0 /* nobody holds it */
1991 || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) {
sewardje663cb92002-04-12 10:26:32 +00001992 vg_threads[tid].m_edx = EPERM;
1993 return;
1994 }
1995
sewardjf8f819e2002-04-17 23:21:37 +00001996 /* If it's a multiply-locked recursive mutex, just decrement the
1997 lock count and return. */
sewardj604ec3c2002-04-18 22:38:41 +00001998 if (mutex->__m_count > 1) {
1999 vg_assert(mutex->__m_kind == PTHREAD_MUTEX_RECURSIVE_NP);
2000 mutex->__m_count --;
sewardjf8f819e2002-04-17 23:21:37 +00002001 vg_threads[tid].m_edx = 0; /* success */
2002 return;
2003 }
2004
sewardj604ec3c2002-04-18 22:38:41 +00002005 /* Now we're sure it is locked exactly once, and by the thread who
sewardjf8f819e2002-04-17 23:21:37 +00002006 is now doing an unlock on it. */
sewardj604ec3c2002-04-18 22:38:41 +00002007 vg_assert(mutex->__m_count == 1);
sewardj6072c362002-04-19 14:40:57 +00002008 vg_assert((ThreadId)mutex->__m_owner == tid);
sewardjf8f819e2002-04-17 23:21:37 +00002009
sewardj3b5d8862002-04-20 13:53:23 +00002010 /* Release at max one thread waiting on this mutex. */
2011 release_one_thread_waiting_on_mutex ( mutex, "pthread_mutex_lock" );
sewardje663cb92002-04-12 10:26:32 +00002012
sewardj3b5d8862002-04-20 13:53:23 +00002013 /* Our (tid's) pth_unlock() returns with 0 (success). */
sewardje663cb92002-04-12 10:26:32 +00002014 vg_threads[tid].m_edx = 0; /* Success. */
2015}
2016
2017
sewardj6072c362002-04-19 14:40:57 +00002018/* -----------------------------------------------------------
2019 CONDITION VARIABLES
2020 -------------------------------------------------------- */
sewardje663cb92002-04-12 10:26:32 +00002021
sewardj6072c362002-04-19 14:40:57 +00002022/* The relevant native types are as follows:
2023 (copied from /usr/include/bits/pthreadtypes.h)
sewardj77e466c2002-04-14 02:29:29 +00002024
sewardj6072c362002-04-19 14:40:57 +00002025 -- Conditions (not abstract because of PTHREAD_COND_INITIALIZER
2026 typedef struct
2027 {
2028 struct _pthread_fastlock __c_lock; -- Protect against concurrent access
2029 _pthread_descr __c_waiting; -- Threads waiting on this condition
2030 } pthread_cond_t;
sewardj77e466c2002-04-14 02:29:29 +00002031
sewardj6072c362002-04-19 14:40:57 +00002032 -- Attribute for conditionally variables.
2033 typedef struct
2034 {
2035 int __dummy;
2036 } pthread_condattr_t;
sewardj77e466c2002-04-14 02:29:29 +00002037
sewardj6072c362002-04-19 14:40:57 +00002038 #define PTHREAD_COND_INITIALIZER {__LOCK_INITIALIZER, 0}
sewardj77e466c2002-04-14 02:29:29 +00002039
sewardj3b5d8862002-04-20 13:53:23 +00002040 We don't use any fields of pthread_cond_t for anything at all.
2041 Only the identity of the CVs is important.
sewardj6072c362002-04-19 14:40:57 +00002042
2043 Linux pthreads supports no attributes on condition variables, so we
sewardj3b5d8862002-04-20 13:53:23 +00002044 don't need to think too hard there. */
sewardj6072c362002-04-19 14:40:57 +00002045
sewardj77e466c2002-04-14 02:29:29 +00002046
sewardj5f07b662002-04-23 16:52:51 +00002047static
2048void do_pthread_cond_timedwait_TIMEOUT ( ThreadId tid )
2049{
2050 Char msg_buf[100];
2051 pthread_mutex_t* mx;
2052 pthread_cond_t* cv;
2053
2054 vg_assert(is_valid_tid(tid)
2055 && vg_threads[tid].status == VgTs_WaitCV
2056 && vg_threads[tid].awaken_at != 0xFFFFFFFF);
2057 mx = vg_threads[tid].associated_mx;
2058 vg_assert(mx != NULL);
2059 cv = vg_threads[tid].associated_cv;
2060 vg_assert(cv != NULL);
2061
2062 if (mx->__m_owner == VG_INVALID_THREADID) {
2063 /* Currently unheld; hand it out to thread tid. */
2064 vg_assert(mx->__m_count == 0);
2065 vg_threads[tid].status = VgTs_Runnable;
2066 vg_threads[tid].m_edx = ETIMEDOUT;
2067 /* pthread_cond_wait return value */
2068 vg_threads[tid].associated_cv = NULL;
2069 vg_threads[tid].associated_mx = NULL;
2070 mx->__m_owner = (_pthread_descr)tid;
2071 mx->__m_count = 1;
2072
2073 if (VG_(clo_trace_pthread_level) >= 1) {
2074 VG_(sprintf)(msg_buf, "pthread_cond_timedwai cv %p: TIMEOUT with mx %p",
2075 cv, mx );
2076 print_pthread_event(tid, msg_buf);
2077 }
2078 } else {
2079 /* Currently held. Make thread tid be blocked on it. */
2080 vg_assert(mx->__m_count > 0);
2081 vg_threads[tid].status = VgTs_WaitMX;
2082 vg_threads[tid].m_edx = ETIMEDOUT;
2083 /* pthread_cond_wait return value */
2084 vg_threads[tid].associated_cv = NULL;
2085 vg_threads[tid].associated_mx = mx;
2086 if (VG_(clo_trace_pthread_level) >= 1) {
2087 VG_(sprintf)(msg_buf,
2088 "pthread_cond_timedwai cv %p: TIMEOUT -> BLOCK for mx %p",
2089 cv, mx );
2090 print_pthread_event(tid, msg_buf);
2091 }
2092
2093 }
2094}
2095
2096
sewardj3b5d8862002-04-20 13:53:23 +00002097static
2098void release_N_threads_waiting_on_cond ( pthread_cond_t* cond,
2099 Int n_to_release,
2100 Char* caller )
2101{
2102 Int i;
2103 Char msg_buf[100];
2104 pthread_mutex_t* mx;
2105
2106 while (True) {
2107 if (n_to_release == 0)
2108 return;
2109
2110 /* Find a thread waiting on this CV. */
2111 for (i = 1; i < VG_N_THREADS; i++) {
2112 if (vg_threads[i].status == VgTs_Empty)
2113 continue;
2114 if (vg_threads[i].status == VgTs_WaitCV
2115 && vg_threads[i].associated_cv == cond)
2116 break;
2117 }
2118 vg_assert(i <= VG_N_THREADS);
2119
2120 if (i == VG_N_THREADS) {
2121 /* Nobody else is waiting on it. */
2122 return;
2123 }
2124
2125 mx = vg_threads[i].associated_mx;
2126 vg_assert(mx != NULL);
2127
2128 if (mx->__m_owner == VG_INVALID_THREADID) {
2129 /* Currently unheld; hand it out to thread i. */
2130 vg_assert(mx->__m_count == 0);
2131 vg_threads[i].status = VgTs_Runnable;
2132 vg_threads[i].associated_cv = NULL;
2133 vg_threads[i].associated_mx = NULL;
2134 mx->__m_owner = (_pthread_descr)i;
2135 mx->__m_count = 1;
sewardj5f07b662002-04-23 16:52:51 +00002136 /* .m_edx already holds pth_cond_wait success value (0) */
sewardj3b5d8862002-04-20 13:53:23 +00002137
2138 if (VG_(clo_trace_pthread_level) >= 1) {
2139 VG_(sprintf)(msg_buf, "%s cv %p: RESUME with mx %p",
2140 caller, cond, mx );
2141 print_pthread_event(i, msg_buf);
2142 }
2143
2144 } else {
2145 /* Currently held. Make thread i be blocked on it. */
sewardj5f07b662002-04-23 16:52:51 +00002146 vg_assert(mx->__m_count > 0);
sewardj3b5d8862002-04-20 13:53:23 +00002147 vg_threads[i].status = VgTs_WaitMX;
2148 vg_threads[i].associated_cv = NULL;
2149 vg_threads[i].associated_mx = mx;
sewardj5f07b662002-04-23 16:52:51 +00002150 vg_threads[i].m_edx = 0; /* pth_cond_wait success value */
sewardj3b5d8862002-04-20 13:53:23 +00002151
2152 if (VG_(clo_trace_pthread_level) >= 1) {
2153 VG_(sprintf)(msg_buf, "%s cv %p: BLOCK for mx %p",
2154 caller, cond, mx );
2155 print_pthread_event(i, msg_buf);
2156 }
2157
2158 }
2159
2160 n_to_release--;
2161 }
2162}
2163
2164
2165static
2166void do_pthread_cond_wait ( ThreadId tid,
2167 pthread_cond_t *cond,
sewardj5f07b662002-04-23 16:52:51 +00002168 pthread_mutex_t *mutex,
2169 UInt ms_end )
sewardj3b5d8862002-04-20 13:53:23 +00002170{
2171 Char msg_buf[100];
2172
sewardj5f07b662002-04-23 16:52:51 +00002173 /* If ms_end == 0xFFFFFFFF, wait forever (no timeout). Otherwise,
2174 ms_end is the ending millisecond. */
2175
sewardj3b5d8862002-04-20 13:53:23 +00002176 /* pre: mutex should be a valid mutex and owned by tid. */
2177 if (VG_(clo_trace_pthread_level) >= 2) {
sewardj5f07b662002-04-23 16:52:51 +00002178 VG_(sprintf)(msg_buf, "pthread_cond_wait cv %p, mx %p, end %d ...",
2179 cond, mutex, ms_end );
sewardj3b5d8862002-04-20 13:53:23 +00002180 print_pthread_event(tid, msg_buf);
2181 }
2182
2183 /* Paranoia ... */
2184 vg_assert(is_valid_tid(tid)
2185 && vg_threads[tid].status == VgTs_Runnable);
2186
2187 if (mutex == NULL || cond == NULL) {
2188 vg_threads[tid].m_edx = EINVAL;
2189 return;
2190 }
2191
2192 /* More paranoia ... */
2193 switch (mutex->__m_kind) {
sewardj2a1dcce2002-04-22 12:45:25 +00002194# ifndef GLIBC_2_1
sewardj3b5d8862002-04-20 13:53:23 +00002195 case PTHREAD_MUTEX_TIMED_NP:
sewardj2a1dcce2002-04-22 12:45:25 +00002196 case PTHREAD_MUTEX_ADAPTIVE_NP:
2197# endif
sewardj3b5d8862002-04-20 13:53:23 +00002198 case PTHREAD_MUTEX_RECURSIVE_NP:
2199 case PTHREAD_MUTEX_ERRORCHECK_NP:
sewardj3b5d8862002-04-20 13:53:23 +00002200 if (mutex->__m_count >= 0) break;
2201 /* else fall thru */
2202 default:
2203 vg_threads[tid].m_edx = EINVAL;
2204 return;
2205 }
2206
2207 /* Barf if we don't currently hold the mutex. */
2208 if (mutex->__m_count == 0 /* nobody holds it */
2209 || (ThreadId)mutex->__m_owner != tid /* we don't hold it */) {
2210 vg_threads[tid].m_edx = EINVAL;
2211 return;
2212 }
2213
2214 /* Queue ourselves on the condition. */
2215 vg_threads[tid].status = VgTs_WaitCV;
2216 vg_threads[tid].associated_cv = cond;
2217 vg_threads[tid].associated_mx = mutex;
sewardj5f07b662002-04-23 16:52:51 +00002218 vg_threads[tid].awaken_at = ms_end;
sewardj3b5d8862002-04-20 13:53:23 +00002219
2220 if (VG_(clo_trace_pthread_level) >= 1) {
2221 VG_(sprintf)(msg_buf,
2222 "pthread_cond_wait cv %p, mx %p: BLOCK",
2223 cond, mutex );
2224 print_pthread_event(tid, msg_buf);
2225 }
2226
2227 /* Release the mutex. */
2228 release_one_thread_waiting_on_mutex ( mutex, "pthread_cond_wait " );
2229}
2230
2231
2232static
2233void do_pthread_cond_signal_or_broadcast ( ThreadId tid,
2234 Bool broadcast,
2235 pthread_cond_t *cond )
2236{
2237 Char msg_buf[100];
2238 Char* caller
2239 = broadcast ? "pthread_cond_broadcast"
2240 : "pthread_cond_signal ";
2241
2242 if (VG_(clo_trace_pthread_level) >= 2) {
2243 VG_(sprintf)(msg_buf, "%s cv %p ...",
2244 caller, cond );
2245 print_pthread_event(tid, msg_buf);
2246 }
2247
2248 /* Paranoia ... */
2249 vg_assert(is_valid_tid(tid)
2250 && vg_threads[tid].status == VgTs_Runnable);
2251
2252 if (cond == NULL) {
2253 vg_threads[tid].m_edx = EINVAL;
2254 return;
2255 }
2256
2257 release_N_threads_waiting_on_cond (
2258 cond,
2259 broadcast ? VG_N_THREADS : 1,
2260 caller
2261 );
2262
2263 vg_threads[tid].m_edx = 0; /* success */
2264}
2265
sewardj77e466c2002-04-14 02:29:29 +00002266
sewardj5f07b662002-04-23 16:52:51 +00002267/* -----------------------------------------------------------
2268 THREAD SPECIFIC DATA
2269 -------------------------------------------------------- */
2270
2271static __inline__
2272Bool is_valid_key ( ThreadKey k )
2273{
2274 /* k unsigned; hence no < 0 check */
2275 if (k >= VG_N_THREAD_KEYS) return False;
2276 if (!vg_thread_keys[k].inuse) return False;
2277 return True;
2278}
2279
2280static
2281void do_pthread_key_create ( ThreadId tid,
2282 pthread_key_t* key,
2283 void (*destructor)(void*) )
2284{
2285 Int i;
2286 Char msg_buf[100];
2287
2288 if (VG_(clo_trace_pthread_level) >= 1) {
2289 VG_(sprintf)(msg_buf, "pthread_key_create *key %p, destr %p",
2290 key, destructor );
2291 print_pthread_event(tid, msg_buf);
2292 }
2293
2294 vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
2295 vg_assert(is_valid_tid(tid)
2296 && vg_threads[tid].status == VgTs_Runnable);
2297
2298 for (i = 0; i < VG_N_THREAD_KEYS; i++)
2299 if (!vg_thread_keys[i].inuse)
2300 break;
2301
2302 if (i == VG_N_THREAD_KEYS) {
2303 /* vg_threads[tid].m_edx = EAGAIN;
2304 return;
2305 */
2306 VG_(panic)("pthread_key_create: VG_N_THREAD_KEYS is too low;"
2307 " increase and recompile");
2308 }
2309
2310 vg_thread_keys[i].inuse = True;
2311 /* TODO: check key for addressibility */
2312 *key = i;
2313 vg_threads[tid].m_edx = 0;
2314}
2315
2316
2317static
2318void do_pthread_key_delete ( ThreadId tid, pthread_key_t key )
2319{
2320 Char msg_buf[100];
2321 if (VG_(clo_trace_pthread_level) >= 1) {
2322 VG_(sprintf)(msg_buf, "pthread_key_delete key %d",
2323 key );
2324 print_pthread_event(tid, msg_buf);
2325 }
2326
2327 vg_assert(is_valid_tid(tid)
2328 && vg_threads[tid].status == VgTs_Runnable);
2329
2330 if (!is_valid_key(key)) {
2331 vg_threads[tid].m_edx = EINVAL;
2332 return;
2333 }
2334
2335 vg_thread_keys[key].inuse = False;
2336
2337 /* Optional. We're not required to do this, although it shouldn't
2338 make any difference to programs which use the key/specifics
2339 functions correctly. */
sewardj3b13f0e2002-04-25 20:17:29 +00002340# if 1
sewardj5f07b662002-04-23 16:52:51 +00002341 for (tid = 1; tid < VG_N_THREADS; tid++) {
2342 if (vg_threads[tid].status != VgTs_Empty)
2343 vg_threads[tid].specifics[key] = NULL;
2344 }
sewardj3b13f0e2002-04-25 20:17:29 +00002345# endif
sewardj5f07b662002-04-23 16:52:51 +00002346}
2347
2348
2349static
2350void do_pthread_getspecific ( ThreadId tid, pthread_key_t key )
2351{
2352 Char msg_buf[100];
2353 if (VG_(clo_trace_pthread_level) >= 1) {
2354 VG_(sprintf)(msg_buf, "pthread_getspecific key %d",
2355 key );
2356 print_pthread_event(tid, msg_buf);
2357 }
2358
2359 vg_assert(is_valid_tid(tid)
2360 && vg_threads[tid].status == VgTs_Runnable);
2361
2362 if (!is_valid_key(key)) {
2363 vg_threads[tid].m_edx = (UInt)NULL;
2364 return;
2365 }
2366
2367 vg_threads[tid].m_edx = (UInt)vg_threads[tid].specifics[key];
2368}
2369
2370
2371static
2372void do_pthread_setspecific ( ThreadId tid,
2373 pthread_key_t key,
2374 void *pointer )
2375{
2376 Char msg_buf[100];
2377 if (VG_(clo_trace_pthread_level) >= 1) {
2378 VG_(sprintf)(msg_buf, "pthread_setspecific key %d, ptr %p",
2379 key, pointer );
2380 print_pthread_event(tid, msg_buf);
2381 }
2382
2383 vg_assert(is_valid_tid(tid)
2384 && vg_threads[tid].status == VgTs_Runnable);
2385
2386 if (!is_valid_key(key)) {
2387 vg_threads[tid].m_edx = EINVAL;
2388 return;
2389 }
2390
2391 vg_threads[tid].specifics[key] = pointer;
2392 vg_threads[tid].m_edx = 0;
2393}
2394
2395
sewardje663cb92002-04-12 10:26:32 +00002396/* ---------------------------------------------------------------------
2397 Handle non-trivial client requests.
2398 ------------------------------------------------------------------ */
2399
2400static
2401void do_nontrivial_clientreq ( ThreadId tid )
2402{
2403 UInt* arg = (UInt*)(vg_threads[tid].m_eax);
2404 UInt req_no = arg[0];
2405 switch (req_no) {
2406
2407 case VG_USERREQ__PTHREAD_CREATE:
2408 do_pthread_create( tid,
2409 (pthread_t*)arg[1],
2410 (pthread_attr_t*)arg[2],
2411 (void*(*)(void*))arg[3],
2412 (void*)arg[4] );
2413 break;
2414
sewardjbc5b99f2002-04-13 00:08:51 +00002415 case VG_USERREQ__PTHREAD_RETURNS:
2416 handle_pthread_return( tid, (void*)arg[1] );
sewardje663cb92002-04-12 10:26:32 +00002417 break;
2418
2419 case VG_USERREQ__PTHREAD_JOIN:
2420 do_pthread_join( tid, arg[1], (void**)(arg[2]) );
2421 break;
2422
sewardje663cb92002-04-12 10:26:32 +00002423 case VG_USERREQ__PTHREAD_CANCEL:
2424 do_pthread_cancel( tid, (pthread_t)(arg[1]) );
2425 break;
2426
sewardj3b5d8862002-04-20 13:53:23 +00002427 case VG_USERREQ__PTHREAD_EXIT:
2428 do_pthread_exit( tid, (void*)(arg[1]) );
2429 break;
2430
2431 case VG_USERREQ__PTHREAD_COND_WAIT:
2432 do_pthread_cond_wait( tid,
2433 (pthread_cond_t *)(arg[1]),
sewardj5f07b662002-04-23 16:52:51 +00002434 (pthread_mutex_t *)(arg[2]),
2435 0xFFFFFFFF /* no timeout */ );
2436 break;
2437
2438 case VG_USERREQ__PTHREAD_COND_TIMEDWAIT:
2439 do_pthread_cond_wait( tid,
2440 (pthread_cond_t *)(arg[1]),
2441 (pthread_mutex_t *)(arg[2]),
2442 arg[3] /* timeout millisecond point */ );
sewardj3b5d8862002-04-20 13:53:23 +00002443 break;
2444
2445 case VG_USERREQ__PTHREAD_COND_SIGNAL:
2446 do_pthread_cond_signal_or_broadcast(
2447 tid,
2448 False, /* signal, not broadcast */
2449 (pthread_cond_t *)(arg[1]) );
2450 break;
2451
2452 case VG_USERREQ__PTHREAD_COND_BROADCAST:
2453 do_pthread_cond_signal_or_broadcast(
2454 tid,
2455 True, /* broadcast, not signal */
2456 (pthread_cond_t *)(arg[1]) );
2457 break;
2458
sewardj5f07b662002-04-23 16:52:51 +00002459 case VG_USERREQ__PTHREAD_KEY_CREATE:
2460 do_pthread_key_create ( tid,
2461 (pthread_key_t*)(arg[1]),
2462 (void(*)(void*))(arg[2]) );
2463 break;
2464
2465 case VG_USERREQ__PTHREAD_KEY_DELETE:
2466 do_pthread_key_delete ( tid,
2467 (pthread_key_t)(arg[1]) );
2468 break;
2469
sewardj5f07b662002-04-23 16:52:51 +00002470 case VG_USERREQ__PTHREAD_SETSPECIFIC:
2471 do_pthread_setspecific ( tid,
2472 (pthread_key_t)(arg[1]),
2473 (void*)(arg[2]) );
2474 break;
2475
sewardje663cb92002-04-12 10:26:32 +00002476 case VG_USERREQ__MAKE_NOACCESS:
2477 case VG_USERREQ__MAKE_WRITABLE:
2478 case VG_USERREQ__MAKE_READABLE:
2479 case VG_USERREQ__DISCARD:
2480 case VG_USERREQ__CHECK_WRITABLE:
2481 case VG_USERREQ__CHECK_READABLE:
2482 case VG_USERREQ__MAKE_NOACCESS_STACK:
2483 case VG_USERREQ__RUNNING_ON_VALGRIND:
2484 case VG_USERREQ__DO_LEAK_CHECK:
sewardj8c824512002-04-14 04:16:48 +00002485 vg_threads[tid].m_edx
2486 = VG_(handle_client_request) ( &vg_threads[tid], arg );
sewardje663cb92002-04-12 10:26:32 +00002487 break;
2488
sewardj77e466c2002-04-14 02:29:29 +00002489 case VG_USERREQ__SIGNAL_RETURNS:
2490 handle_signal_return(tid);
2491 break;
sewardj54cacf02002-04-12 23:24:59 +00002492
sewardje663cb92002-04-12 10:26:32 +00002493 default:
2494 VG_(printf)("panic'd on private request = 0x%x\n", arg[0] );
2495 VG_(panic)("handle_private_client_pthread_request: "
2496 "unknown request");
2497 /*NOTREACHED*/
2498 break;
2499 }
2500}
2501
2502
sewardj6072c362002-04-19 14:40:57 +00002503/* ---------------------------------------------------------------------
2504 Sanity checking.
2505 ------------------------------------------------------------------ */
2506
2507/* Internal consistency checks on the sched/pthread structures. */
2508static
2509void scheduler_sanity ( void )
2510{
sewardj3b5d8862002-04-20 13:53:23 +00002511 pthread_mutex_t* mx;
2512 pthread_cond_t* cv;
sewardj6072c362002-04-19 14:40:57 +00002513 Int i;
sewardj5f07b662002-04-23 16:52:51 +00002514
sewardj6072c362002-04-19 14:40:57 +00002515 /* VG_(printf)("scheduler_sanity\n"); */
2516 for (i = 1; i < VG_N_THREADS; i++) {
sewardj3b5d8862002-04-20 13:53:23 +00002517 mx = vg_threads[i].associated_mx;
2518 cv = vg_threads[i].associated_cv;
sewardj6072c362002-04-19 14:40:57 +00002519 if (vg_threads[i].status == VgTs_WaitMX) {
sewardj05553872002-04-20 20:53:17 +00002520 /* If we're waiting on a MX: (1) the mx is not null, (2, 3)
2521 it's actually held by someone, since otherwise this thread
2522 is deadlocked, (4) the mutex's owner is not us, since
2523 otherwise this thread is also deadlocked. The logic in
2524 do_pthread_mutex_lock rejects attempts by a thread to lock
2525 a (non-recursive) mutex which it already owns.
2526
2527 (2) has been seen to fail sometimes. I don't know why.
2528 Possibly to do with signals. */
sewardj3b5d8862002-04-20 13:53:23 +00002529 vg_assert(cv == NULL);
sewardj05553872002-04-20 20:53:17 +00002530 /* 1 */ vg_assert(mx != NULL);
2531 /* 2 */ vg_assert(mx->__m_count > 0);
2532 /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
2533 /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner);
sewardj3b5d8862002-04-20 13:53:23 +00002534 } else
2535 if (vg_threads[i].status == VgTs_WaitCV) {
2536 vg_assert(cv != NULL);
2537 vg_assert(mx != NULL);
sewardj6072c362002-04-19 14:40:57 +00002538 } else {
sewardj05553872002-04-20 20:53:17 +00002539 /* Unfortunately these don't hold true when a sighandler is
2540 running. To be fixed. */
2541 /* vg_assert(cv == NULL); */
2542 /* vg_assert(mx == NULL); */
sewardj6072c362002-04-19 14:40:57 +00002543 }
2544 }
sewardj5f07b662002-04-23 16:52:51 +00002545
2546 for (i = 0; i < VG_N_THREAD_KEYS; i++) {
2547 if (!vg_thread_keys[i].inuse)
2548 vg_assert(vg_thread_keys[i].destructor == NULL);
2549 }
sewardj6072c362002-04-19 14:40:57 +00002550}
2551
2552
sewardje663cb92002-04-12 10:26:32 +00002553/*--------------------------------------------------------------------*/
2554/*--- end vg_scheduler.c ---*/
2555/*--------------------------------------------------------------------*/