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 const SilcScheduleOps schedule_ops;
25 #define SILC_WM_EVENT WM_USER + 1
28 HWND window; /* Hidden window for receiving socket events */
29 WNDCLASS wclass; /* Window class */
30 HANDLE wakeup_sema; /* Scheduler wakeup semaphore */
31 unsigned int in_schedule : 1;
32 } *SilcWin32Scheduler;
34 /* Our select() call. This simply waits for some events to happen. It also
35 dispatches window messages so it can be used as the main loop of Windows
36 application. This doesn't wait for fds or sockets but does receive
37 notifications via wakeup semaphore when event occurs on some fd or socket.
38 The fds and sockets are scheduled via WSAAsyncSelect. */
40 int silc_select(SilcSchedule schedule, void *context)
42 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
43 SilcHashTableList htl;
44 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
46 LONG timeo = INFINITE;
51 if (!internal->in_schedule) {
52 internal->in_schedule = TRUE;
53 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
56 /* Add wakeup semaphore to events */
57 handles[nhandles++] = (HANDLE)internal->wakeup_sema;
60 if (schedule->has_timeout)
61 timeo = ((schedule->timeout.tv_sec * 1000) +
62 (schedule->timeout.tv_usec / 1000));
64 SILC_SCHEDULE_UNLOCK(schedule);
66 curtime = GetTickCount();
67 ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
70 if (ready == WAIT_FAILED) {
71 /* Wait failed with error */
72 SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
73 SILC_SCHEDULE_LOCK(schedule);
74 internal->in_schedule = FALSE;
77 } else if (ready >= WAIT_ABANDONED_0 &&
78 ready < WAIT_ABANDONED_0 + nhandles) {
79 /* Signal abandoned */
80 SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
81 SILC_SCHEDULE_LOCK(schedule);
82 internal->in_schedule = FALSE;
85 } else if (ready == WAIT_TIMEOUT) {
87 SILC_LOG_DEBUG(("Timeout"));
88 SILC_SCHEDULE_LOCK(schedule);
89 internal->in_schedule = FALSE;
92 } else if (ready == WAIT_OBJECT_0 + nhandles) {
93 /* Windows messages. The MSDN online says that if the application
94 creates a window then its main loop (and we're assuming that
95 it is our SILC Scheduler) must handle the Windows messages, so do
96 it here as the MSDN suggests. */
97 SILC_LOG_DEBUG(("Dispatch window messages"));
98 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
99 TranslateMessage(&msg);
100 DispatchMessage(&msg);
103 /* If timeout is set then we must update the timeout since we won't
104 return and we will give the wait another try. */
105 if (timeo != INFINITE) {
106 timeo -= GetTickCount() - curtime;
107 curtime = GetTickCount();
112 /* Give the wait another try */
115 } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
116 /* Some event occurred. */
117 SILC_LOG_DEBUG(("Dispatch events"));
118 SILC_SCHEDULE_LOCK(schedule);
119 internal->in_schedule = FALSE;
120 return silc_list_count(schedule->fd_dispatch) + 1;
123 internal->in_schedule = FALSE;
127 /* Window callback. We get here when some event occurs on file descriptor
128 or socket that has been scheduled. We add them to dispatch queue and
129 notify the scheduler handle them. */
131 static LRESULT CALLBACK
132 silc_schedule_wnd_proc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
134 SilcSchedule schedule = (SilcSchedule)GetWindowLong(hwnd, GWL_USERDATA);
135 SilcWin32Scheduler internal;
141 internal = (SilcWin32Scheduler)schedule->internal;
142 fd = (SilcUInt32)wParam;
144 SILC_LOG_DEBUG(("SILC_WM_EVENT fd %d", fd));
145 SILC_SCHEDULE_LOCK(schedule);
147 if (!internal->in_schedule) {
148 /* We are not in scheduler so set up the dispatch queue now */
149 internal->in_schedule = TRUE;
150 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
153 /* Find task by fd */
154 if (!silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
155 NULL, (void *)&task)) {
156 SILC_SCHEDULE_UNLOCK(schedule);
160 /* Ignore the event if the task not valid anymore */
161 if (!task->header.valid || !task->events) {
162 SILC_SCHEDULE_UNLOCK(schedule);
167 switch (WSAGETSELECTEVENT(lParam)) {
170 SILC_LOG_DEBUG(("FD_READ"));
171 task->revents |= SILC_TASK_READ;
172 silc_list_add(schedule->fd_dispatch, task);
176 SILC_LOG_DEBUG(("FD_WRITE"));
177 task->revents |= SILC_TASK_WRITE;
178 silc_list_add(schedule->fd_dispatch, task);
182 SILC_LOG_DEBUG(("FD_ACCEPT"));
183 task->revents |= SILC_TASK_READ;
184 silc_list_add(schedule->fd_dispatch, task);
191 /* Wakeup scheduler */
192 ReleaseSemaphore(internal->wakeup_sema, 1, NULL);
194 SILC_SCHEDULE_UNLOCK(schedule);
202 return DefWindowProc(hwnd, wMsg, wParam, lParam);
207 static SilcBool silc_net_win32_init(void)
209 int ret, sopt = SO_SYNCHRONOUS_NONALERT;
211 WORD ver = MAKEWORD(2, 2);
213 ret = WSAStartup(ver, &wdata);
217 /* Allow using the SOCKET's as file descriptors so that we can poll
218 them with SILC Scheduler. */
219 ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt,
227 /* Uninit Winsock2 */
229 static void silc_net_win32_uninit(void)
234 /* Initializes the platform specific scheduler. This for example initializes
235 the wakeup mechanism of the scheduler. In multi-threaded environment
236 the scheduler needs to be wakenup when tasks are added or removed from
237 the task queues. Returns context to the platform specific scheduler. */
239 void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
241 SilcWin32Scheduler internal;
244 /* Initialize Winsock */
245 silc_net_win32_init();
247 internal = silc_calloc(1, sizeof(*internal));
251 schedule->max_tasks = MAXIMUM_WAIT_OBJECTS;
253 /* Create hidden window. We need window so that we can use WSAAsyncSelect
254 to set socket events. */
255 silc_snprintf(n, sizeof(n), "SilcSchedule-%p", schedule);
256 internal->wclass.lpfnWndProc = silc_schedule_wnd_proc;
257 internal->wclass.cbWndExtra = sizeof(schedule);
258 internal->wclass.lpszClassName = (CHAR *)strdup(n);
259 RegisterClass(&internal->wclass);
260 internal->window = CreateWindow((CHAR *)internal->wclass.lpszClassName, "",
261 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
262 if (!internal->window) {
263 SILC_LOG_ERROR(("Could not create hidden window for scheduler"));
264 DestroyWindow(internal->window);
265 UnregisterClass((CHAR *)n, NULL);
270 /* Set the scheduler as the window's context */
271 SetWindowLong(internal->window, GWL_USERDATA, (void *)schedule);
272 SetWindowPos(internal->window, HWND_BOTTOM, 0, 0, 0, 0, SWP_FRAMECHANGED);
274 internal->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
275 if (!internal->wakeup_sema) {
276 SILC_LOG_ERROR(("Could not create wakeup semaphore for scheduler"));
281 return (void *)internal;
284 /* Uninitializes the platform specific scheduler context. */
286 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
288 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
294 silc_snprintf(n, sizeof(n), "SilcSchedule-%p", schedule);
295 DestroyWindow(internal->window);
296 UnregisterClass((CHAR *)n, NULL);
298 CloseHandle(internal->wakeup_sema);
299 silc_net_win32_uninit();
304 /* Schedule `task' with events `event_mask'. Zero `event_mask' unschedules. */
306 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
309 SilcTaskEvent event_mask)
311 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
317 SILC_LOG_DEBUG(("Scheduling fd %d for events %d", task->fd, event_mask));
319 if (event_mask & SILC_TASK_READ)
320 events |= FD_READ | FD_ACCEPT | FD_OOB;
321 if (event_mask & SILC_TASK_WRITE)
324 /* Schedule for events. The silc_schedule_wnd_proc will be called to
325 deliver the events for this fd. */
326 WSAAsyncSelect(task->fd, internal->window, SILC_WM_EVENT, events);
331 /* Wakes up the scheduler */
333 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
336 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
337 ReleaseSemaphore(internal->wakeup_sema, 1, NULL);
338 #endif /* SILC_THREADS */
341 /* Register signal */
343 void silc_schedule_internal_signal_register(SilcSchedule schedule,
346 SilcTaskCallback callback,
347 void *callback_context)
352 /* Unregister signal */
354 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
361 /* Call all signals */
363 void silc_schedule_internal_signals_call(SilcSchedule schedule,
369 /* Block registered signals in scheduler. */
371 void silc_schedule_internal_signals_block(SilcSchedule schedule,
377 /* Unblock registered signals in schedule. */
379 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
385 const SilcScheduleOps schedule_ops =
387 silc_schedule_internal_init,
388 silc_schedule_internal_uninit,
390 silc_schedule_internal_schedule_fd,
391 silc_schedule_internal_wakeup,
392 silc_schedule_internal_signal_register,
393 silc_schedule_internal_signal_unregister,
394 silc_schedule_internal_signals_call,
395 silc_schedule_internal_signals_block,
396 silc_schedule_internal_signals_unblock,