/* * Schedule the next runnable fiber. * Assumes the _runq is NOT empty */ void _fiber_schedule_next (void) { thread_t *thr; int i; if (_current_fiber->thr_status == RUNNING) _fiber_status (_current_fiber, RUNNABLE); #ifdef EXPIRIMENTAL while (_thread_num_runnable == 0) _fiber_event_loop (); #endif thr = NULL; for (i = MAX_PRIORITY; --i >= 0; ) if (_runq[i].thq_count) { thr = (thread_t *) _runq[i].thq_head.thr_next; break; } assert (thr != NULL); _fiber_status (thr, RUNNING); if (_current_fiber != thr) _fiber_switch (thr); }
void semaphore_free (semaphore_t *sem) { thread_t *thr; while ((thr = thread_queue_from (&sem->sem_waiting)) != NULL) _fiber_status (thr, RUNNABLE); dk_free (sem, sizeof (semaphore_t)); }
DK_INLINE int semaphore_enter (semaphore_t * sem) { if (sem->sem_entry_count) sem->sem_entry_count--; else { thread_queue_to (&sem->sem_waiting, _current_fiber); _fiber_status (_current_fiber, WAITSEM); _fiber_schedule_next (); } return 0; }
thread_t * thread_create ( thread_init_func initial_function, unsigned long stack_size, void *init_arg) { thread_t *thr; assert (_main_thread != NULL); if (stack_size == 0) stack_size = THREAD_STACK_SIZE; #if (SIZEOF_VOID_P == 8) stack_size *= 2; #endif #if defined (__x86_64 ) && defined (SOLARIS) /*GK: the LDAP on that platform requires that */ stack_size *= 2; #endif /* Any free threads with the right stack size? */ for (thr = (thread_t *) _deadq.thq_head.thr_next; thr != (thread_t *) &_deadq.thq_head; thr = (thread_t *) thr->thr_hdr.thr_next) { if (thr->thr_stack_size >= stack_size) break; } if (thr == (thread_t *) &_deadq.thq_head) { /* No free fiber, create a new one */ thr = thread_allocate (); _fiber_for_thread (thr, stack_size); _thread_num_total++; } else { /* Set new context for the thread */ memcpy (thr->thr_context, thr->thr_init_context, sizeof (jmp_buf)); } thr->thr_initial_function = initial_function; thr->thr_initial_argument = init_arg; thread_set_priority (thr, NORMAL_PRIORITY); _thread_init_attributes (thr); _fiber_status (thr, RUNNABLE); return thr; }
void thread_exit (int n) { if (_current_fiber->thr_attached) return; _fiber_status (_current_fiber, DEAD); _thread_free_attributes (_current_fiber); if (_current_fiber == _main_thread) exit (n); _fiber_schedule_next (); /* could come here using win fibers */ longjmp (_current_fiber->thr_init_context, 1); }
static void _fiber_timeout (void *arg) { thread_t *thr = (thread_t *) arg; thr->thr_retcode = -1; #ifdef WIN32 thr->thr_err = WSAETIMEDOUT; #else thr->thr_err = ETIMEDOUT; #endif _fiber_status (thr, RUNNABLE); }
int mutex_enter (dk_mutex_t *mtx) #endif { #ifndef MTX_DEBUG return semaphore_enter (mtx->mtx_handle); #else semaphore_t *sem = (semaphore_t *) mtx->mtx_handle; #ifdef MALLOC_DEBUG if (_current_fiber == NULL) { assert (mtx == _dbgmal_mtx); return semaphore_enter (sem); } #endif assert (_current_fiber != NULL); if (sem->sem_entry_count) { assert (sem->sem_entry_count == 1); assert (mtx->mtx_owner == NULL); sem->sem_entry_count--; } else { assert (mtx->mtx_owner != _current_fiber); thread_queue_to (&sem->sem_waiting, _current_fiber); _fiber_status (_current_fiber, WAITSEM); _fiber_schedule_next (); assert (sem->sem_entry_count == 0); } assert (mtx->mtx_owner == NULL); if (mtx->mtx_entry_check && !mtx->mtx_entry_check (mtx, THREAD_CURRENT_THREAD, mtx->mtx_entry_check_cd)) GPF_T1 ("Mtx entry check fail"); mtx->mtx_owner = _current_fiber; mtx->mtx_entry_file = (char *) file; mtx->mtx_entry_line = line; return 0; #endif }
DK_INLINE void semaphore_leave (semaphore_t *sem) #endif { thread_t *thr; if (sem->sem_entry_count) sem->sem_entry_count++; else { thr = thread_queue_from (&sem->sem_waiting); if (thr) { assert (thr->thr_status == WAITSEM); _fiber_status (thr, RUNNABLE); } else sem->sem_entry_count++; } }
/* * The main thread must call this function to convert itself into a fiber. */ thread_t * thread_initial (unsigned long stack_size) { static unsigned int marker = THREAD_STACK_MARKER; if (_current_fiber) return _current_fiber; else { NEW_VARZ (thread_t, thr); assert (_current_fiber == NULL); _main_thread = _current_fiber = thr; _sched_init (); if (stack_size == 0) stack_size = MAIN_STACK_SIZE; #if (SIZEOF_VOID_P == 8) stack_size *= 2; #endif #if defined (__x86_64 ) && defined (SOLARIS) /*GK: the LDAP on that platform requires that */ stack_size *= 2; #endif thr->thr_stack_marker = ▮ thr->thr_sem = semaphore_allocate (0); thr->thr_schedule_sem = semaphore_allocate (0); thread_set_priority (thr, NORMAL_PRIORITY); _thread_init_attributes (thr); _fiber_for_thread (thr, stack_size); _fiber_status (thr, RUNNING); return thr; } }
int _fiber_sleep (void *event, TVAL timeout) { thread_t *thr = _current_fiber; assert (thr->thr_status == RUNNING); thr->thr_err = 0; thr->thr_retcode = 0; /* set a timer */ assert (thr->thr_timer == NULL); if (timeout != TV_INFINITE) thr->thr_timer = timer_queue_new_timer (_timerq, timeout, 0, _fiber_timeout, thr); if (event == NULL) event = &_ev_never; thr->thr_event = event; do { _fiber_status (thr, WAITEVENT); _fiber_schedule_next (); } while (thr->thr_event == event && thr->thr_err == 0); thr->thr_event = NULL; if (timeout != TV_INFINITE) { timer_deactivate (thr->thr_timer); timer_unref (thr->thr_timer); thr->thr_timer = NULL; } return thr->thr_retcode; }
int thread_signal_cond (void *event) { thread_t *thr; thread_t *next; int count = 0; /* Wake up waiting threads for which event occurred */ for (thr = (thread_t *) _waitq.thq_head.thr_next; thr != (thread_t *) &_waitq.thq_head; thr = next) { next = (thread_t *) thr->thr_hdr.thr_next; if (thr->thr_event == event) { thr->thr_event = NULL; thr->thr_retcode = 0; _fiber_status (thr, RUNNABLE); count++; } } return count; }
void mutex_leave (dk_mutex_t *mtx) #endif { #ifndef MTX_DEBUG semaphore_leave (mtx->mtx_handle); #else semaphore_t *sem = (semaphore_t *) mtx->mtx_handle; thread_t *thr; #ifdef MALLOC_DEBUG if (_current_fiber == NULL) { assert (mtx == _dbgmal_mtx); semaphore_leave (sem); return; } #endif assert (mtx->mtx_owner == _current_fiber); assert (sem->sem_entry_count == 0); mtx->mtx_owner = NULL; if (sem->sem_entry_count) sem->sem_entry_count++; else { thr = thread_queue_from (&sem->sem_waiting); if (thr) { assert (thr->thr_status == WAITSEM); _fiber_status (thr, RUNNABLE); } else sem->sem_entry_count++; } #endif }
/* * This is the idle task, that runs at the lowest possible priority. * It's only called when there are no other fibers ready for scheduling. */ void _fiber_event_loop (void) { /* These are all static, to minimize stack usage */ static fd_set readfds; static fd_set writefds; static int nreadfds; static int nwritefds; static int nfds; static thread_t *thr, *next; static TVAL timeout; static struct timeval tv, *ptv; static int rc; for (;;) { timeout = timer_queue_update (_timerq, timer_queue_time_elapsed (_timerq)); if (_thread_num_runnable) return; FD_ZERO (&readfds); FD_ZERO (&writefds); for (thr = (thread_t *) _waitq.thq_head.thr_next; thr != (thread_t *) &_waitq.thq_head; thr = (thread_t *) thr->thr_hdr.thr_next) { if (thr->thr_nfds) { nreadfds = _fd_set_or (&readfds, &thr->thr_rfds); nwritefds = _fd_set_or (&writefds, &thr->thr_wfds); } } nfds = MAX (nreadfds, nwritefds); if (timeout == 0) ptv = NULL; else { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ptv = &tv; } rc = select (nfds, &readfds, &writefds, NULL, ptv); if (rc == -1) { if (errno != EINTR) GPF_T1 ("select() returned -1"); continue; } timer_queue_update (_timerq, timer_queue_time_elapsed (_timerq)); if (rc > 0) { /* Wake up waiting fibers for which an event occurred */ for (thr = (thread_t *) _waitq.thq_head.thr_next; thr != (thread_t *) &_waitq.thq_head; thr = next) { next = (thread_t *) thr->thr_hdr.thr_next; if (thr->thr_nfds == 0) continue; if (_fd_set_intersect (&thr->thr_rfds, &readfds) || _fd_set_intersect (&thr->thr_wfds, &writefds)) { _fd_set_and (&thr->thr_rfds, &readfds); _fd_set_and (&thr->thr_wfds, &writefds); thr->thr_event = NULL; thr->thr_retcode = 1; _fiber_status (thr, RUNNABLE); } } } break; } }