1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team 1995-2002
5 * Support for concurrent non-blocking I/O and thread waiting.
7 * ---------------------------------------------------------------------------*/
10 /* we're outside the realms of POSIX here... */
11 /* #include "PosixSource.h" */
20 #include "Capability.h"
22 # ifdef HAVE_SYS_TYPES_H
23 # include <sys/types.h>
26 # ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
40 /* There's a clever trick here to avoid problems when the time wraps
41 * around. Since our maximum delay is smaller than 31 bits of ticks
42 * (it's actually 31 bits of microseconds), we can safely check
43 * whether a timer has expired even if our timer will wrap around
44 * before the target is reached, using the following formula:
46 * (int)((uint)current_time - (uint)target_time) < 0
48 * if this is true, then our time has expired.
49 * (idea due to Andy Gill).
52 wakeUpSleepingThreads(lnat ticks)
55 rtsBool flag = rtsFalse;
57 while (sleeping_queue != END_TSO_QUEUE &&
58 (int)(ticks - sleeping_queue->block_info.target) > 0) {
60 sleeping_queue = tso->link;
61 tso->why_blocked = NotBlocked;
62 tso->link = END_TSO_QUEUE;
63 IF_DEBUG(scheduler,debugBelch("Waking up sleeping thread %d\n", tso->id));
64 PUSH_ON_RUN_QUEUE(tso);
70 /* Argument 'wait' says whether to wait for I/O to become available,
71 * or whether to just check and return immediately. If there are
72 * other threads ready to run, we normally do the non-waiting variety,
73 * otherwise we wait (see Schedule.c).
75 * SMP note: must be called with sched_mutex locked.
77 * Windows: select only works on sockets, so this doesn't really work,
78 * though it makes things better than before. MsgWaitForMultipleObjects
79 * should really be used, though it only seems to work for read handles,
84 awaitEvent(rtsBool wait)
86 StgTSO *tso, *prev, *next;
91 rtsBool select_succeeded = rtsTrue;
92 rtsBool unblock_all = rtsFalse;
100 debugBelch("scheduler: checking for threads blocked on I/O");
102 debugBelch(" (waiting)");
107 /* loop until we've woken up some threads. This loop is needed
108 * because the select timing isn't accurate, we sometimes sleep
109 * for a while but not long enough to wake up a thread in
114 ticks = timestamp = getourtimeofday();
115 if (wakeUpSleepingThreads(ticks)) {
121 } else if (sleeping_queue != END_TSO_QUEUE) {
122 min = (sleeping_queue->block_info.target - ticks)
123 * TICK_MILLISECS * 1000;
129 * Collect all of the fd's that we're interested in
134 for(tso = blocked_queue_hd; tso != END_TSO_QUEUE; tso = next) {
137 switch (tso->why_blocked) {
140 int fd = tso->block_info.fd;
141 maxfd = (fd > maxfd) ? fd : maxfd;
148 int fd = tso->block_info.fd;
149 maxfd = (fd > maxfd) ? fd : maxfd;
159 /* Check for any interesting events */
161 tv.tv_sec = min / 1000000;
162 tv.tv_usec = min % 1000000;
164 while ((numFound = select(maxfd+1, &rfd, &wfd, NULL, &tv)) < 0) {
165 if (errno != EINTR) {
166 /* Handle bad file descriptors by unblocking all the
167 waiting threads. Why? Because a thread might have been
168 a bit naughty and closed a file descriptor while another
169 was blocked waiting. This is less-than-good programming
170 practice, but having the RTS as a result fall over isn't
171 acceptable, so we simply unblock all the waiting threads
172 should we see a bad file descriptor & give the threads
173 a chance to clean up their act.
175 Note: assume here that threads becoming unblocked
176 will try to read/write the file descriptor before trying
177 to issue a threadWaitRead/threadWaitWrite again (==> an
178 IOError will result for the thread that's got the bad
179 file descriptor.) Hence, there's no danger of a bad
180 file descriptor being repeatedly select()'ed on, so
183 if ( errno == EBADF ) {
184 unblock_all = rtsTrue;
188 barf("select failed");
192 /* We got a signal; could be one of ours. If so, we need
193 * to start up the signal handler straight away, otherwise
194 * we could block for a long time before the signal is
197 #if defined(RTS_USER_SIGNALS)
198 if (signals_pending()) {
199 startSignalHandlers();
200 return; /* still hold the lock */
204 /* we were interrupted, return to the scheduler immediately.
207 return; /* still hold the lock */
210 /* check for threads that need waking up
212 wakeUpSleepingThreads(getourtimeofday());
214 /* If new runnable threads have arrived, stop waiting for
217 if (run_queue_hd != END_TSO_QUEUE) {
218 return; /* still hold the lock */
222 /* Step through the waiting queue, unblocking every thread that now has
223 * a file descriptor in a ready state.
227 if (select_succeeded || unblock_all) {
228 for(tso = blocked_queue_hd; tso != END_TSO_QUEUE; tso = next) {
230 switch (tso->why_blocked) {
232 ready = unblock_all || FD_ISSET(tso->block_info.fd, &rfd);
235 ready = unblock_all || FD_ISSET(tso->block_info.fd, &wfd);
242 IF_DEBUG(scheduler,debugBelch("Waking up blocked thread %d\n", tso->id));
243 tso->why_blocked = NotBlocked;
244 tso->link = END_TSO_QUEUE;
245 PUSH_ON_RUN_QUEUE(tso);
248 blocked_queue_hd = tso;
256 blocked_queue_hd = blocked_queue_tl = END_TSO_QUEUE;
258 prev->link = END_TSO_QUEUE;
259 blocked_queue_tl = prev;
263 } while (wait && !interrupted && run_queue_hd == END_TSO_QUEUE);