5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
23 /* Our "select()" for WIN32. This mimics the behaviour of select() system
24 call. It does not call the Winsock's select() though. Its functions
25 are derived from GLib's g_poll() and from some old Xemacs's sys_select().
27 This makes following assumptions, which I don't know whether they
30 o SILC_TASK_WRITE is ignored, if set this will return immediately.
31 o If all arguments except timeout are NULL then this will register
32 a timeout with SetTimer and will wait just for Windows messages
34 o MsgWaitForMultipleObjects is used to wait all kind of events, this
35 includes SOCKETs and Windows messages.
36 o All Windows messages are dispatched from this function.
37 o The Operating System has Winsock 2.
41 o http://msdn.microsoft.com/library/default.asp?
42 url=/library/en-us/winui/hh/winui/messques_77zk.asp
43 o http://msdn.microsoft.com/library/default.asp?
44 url=/library/en-us/winsock/hh/winsock/apistart_9g1e.asp
45 o http://msdn.microsoft.com/library/default.asp?
46 url=/library/en-us/dnmgmt/html/msdn_getpeek.asp
47 o http://developer.novell.com/support/winsock/doc/toc.htm
51 int silc_select(SilcSchedule schedule, void *context);
53 SilcHashTableList htl;
55 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
61 silc_hash_table_list(schedule->fd_queue, &htl);
62 while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
65 if (nhandles >= MAXIMUM_WAIT_OBJECTS)
68 if (task->events & SILC_TASK_READ)
69 handles[nhandles++] = (HANDLE)fd;
71 /* If writing then just set the bit and return */
72 if (task->events & SILC_TASK_WRITE) {
73 task->revents = SILC_TASK_WRITE;
79 silc_hash_table_list_reset(&htl);
80 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
82 timeo = (schedule->has_timeout ? ((schedule->timeout.tv_sec * 1000) +
83 (schedule->timeout.tv_usec / 1000))
86 /* If we have nothing to wait and timeout is set then register a timeout
87 and wait just for windows messages. */
88 if (nhandles == 0 && schedule->has_timeout) {
89 SILC_SCHEDULE_UNLOCK(schedule);
90 UINT timer = SetTimer(NULL, 0, timeo, NULL);
91 curtime = GetTickCount();
95 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
96 if (msg.message == WM_TIMER) {
97 KillTimer(NULL, timer);
98 SILC_SCHEDULE_LOCK(schedule);
101 TranslateMessage(&msg);
102 DispatchMessage(&msg);
105 KillTimer(NULL, timer);
106 if (timeo != INFINITE) {
107 timeo -= GetTickCount() - curtime;
108 curtime = GetTickCount();
112 timer = SetTimer(NULL, 0, timeo, NULL);
114 SILC_SCHEDULE_LOCK(schedule);
117 SILC_SCHEDULE_UNLOCK(schedule);
119 curtime = GetTickCount();
120 ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
122 SILC_SCHEDULE_LOCK(schedule);
124 if (ready == WAIT_FAILED) {
125 /* Wait failed with error */
126 SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
128 } else if (ready >= WAIT_ABANDONED_0 &&
129 ready < WAIT_ABANDONED_0 + nhandles) {
130 /* Signal abandoned */
131 SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
133 } else if (ready == WAIT_TIMEOUT) {
136 } else if (ready == WAIT_OBJECT_0 + nhandles) {
137 /* Windows messages. The MSDN online says that if the application
138 creates a window then its main loop (and we're assuming that
139 it is our SILC Scheduler) must handle the Windows messages, so do
140 it here as the MSDN suggests. */
141 SILC_SCHEDULE_UNLOCK(schedule);
142 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
143 TranslateMessage(&msg);
144 DispatchMessage(&msg);
147 /* If timeout is set then we must update the timeout since we won't
148 return and we will give the wait another try. */
149 if (timeo != INFINITE) {
150 timeo -= GetTickCount() - curtime;
151 curtime = GetTickCount();
156 /* Give the wait another try */
158 } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
159 /* Some other event, like SOCKET or something. */
161 /* Go through all fds even though only one was set. This is to avoid
162 starvation of high numbered fds. */
163 nhandles = silc_hash_table_count(schedule->fd_queue);
164 ready -= WAIT_OBJECT_0;
166 if (!silc_hash_table_find(schedule->fd_queue,
167 SILC_32_TO_PTR((SilcUInt32)handles[ready]),
168 NULL, (void *)&task))
171 if (task->header.valid && task->events) {
172 task->revents |= SILC_TASK_READ;
173 silc_list_add(schedule->fd_dispatch, task);
176 /* Check the status of the next handle and set its fd to the fd
177 set if data is available. */
178 SILC_SCHEDULE_UNLOCK(schedule);
179 while (++ready < nhandles)
180 if (WaitForSingleObject(handles[ready], 0) == WAIT_OBJECT_0)
182 SILC_SCHEDULE_LOCK(schedule);
183 } while (ready < nhandles);
185 return silc_list_count(schedule->fd_dispatch);
193 /* Internal wakeup context. */
196 SilcTask wakeup_task;
199 SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
204 #endif /* SILC_THREADS */
206 /* Initializes the platform specific scheduler. This for example initializes
207 the wakeup mechanism of the scheduler. In multi-threaded environment
208 the scheduler needs to be wakenup when tasks are added or removed from
209 the task queues. Returns context to the platform specific scheduler. */
211 void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
214 SilcWin32Wakeup wakeup;
217 schedule->max_tasks = MAXIMUM_WAIT_OBJECTS;
220 wakeup = silc_calloc(1, sizeof(*wakeup));
224 wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
225 if (!wakeup->wakeup_sema) {
230 wakeup->wakeup_task =
231 silc_schedule_task_add(schedule, (int)wakeup->wakeup_sema,
232 silc_schedule_wakeup_cb, wakeup,
234 if (!wakeup->wakeup_task) {
235 CloseHandle(wakeup->wakeup_sema);
240 return (void *)wakeup;
246 /* Uninitializes the platform specific scheduler context. */
248 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
251 SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
256 CloseHandle(wakeup->wakeup_sema);
261 /* Schedule `task' with events `event_mask'. Zero `event_mask' unschedules. */
263 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
266 SilcTaskEvent event_mask)
271 /* Wakes up the scheduler */
273 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
276 SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
281 ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
285 /* Register signal */
287 void silc_schedule_internal_signal_register(SilcSchedule schedule,
290 SilcTaskCallback callback,
291 void *callback_context)
296 /* Unregister signal */
298 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
305 /* Call all signals */
307 void silc_schedule_internal_signals_call(SilcSchedule schedule,
313 /* Block registered signals in scheduler. */
315 void silc_schedule_internal_signals_block(SilcSchedule schedule,
321 /* Unblock registered signals in schedule. */
323 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
329 const SilcScheduleOps schedule_ops =
331 silc_schedule_internal_init,
332 silc_schedule_internal_uninit,
334 silc_schedule_internal_schedule_fd,
335 silc_schedule_internal_wakeup,
336 silc_schedule_internal_signal_register,
337 silc_schedule_internal_signal_unregister,
338 silc_schedule_internal_signals_call,
339 silc_schedule_internal_signals_block,
340 silc_schedule_internal_signals_unblock,