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 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
45 LONG timeo = INFINITE;
50 if (!internal->in_schedule) {
51 internal->in_schedule = TRUE;
52 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
55 /* Add wakeup semaphore to events */
56 handles[nhandles++] = internal->wakeup_sema;
59 if (schedule->has_timeout)
60 timeo = ((schedule->timeout.tv_sec * 1000) +
61 (schedule->timeout.tv_usec / 1000));
63 SILC_SCHEDULE_UNLOCK(schedule);
65 curtime = GetTickCount();
66 ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
69 if (ready == WAIT_FAILED) {
70 /* Wait failed with error */
71 SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
72 SILC_SCHEDULE_LOCK(schedule);
73 internal->in_schedule = FALSE;
76 } else if (ready >= WAIT_ABANDONED_0 &&
77 ready < WAIT_ABANDONED_0 + nhandles) {
78 /* Signal abandoned */
79 SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
80 SILC_SCHEDULE_LOCK(schedule);
81 internal->in_schedule = FALSE;
84 } else if (ready == WAIT_TIMEOUT) {
86 SILC_LOG_DEBUG(("Timeout"));
87 SILC_SCHEDULE_LOCK(schedule);
88 internal->in_schedule = FALSE;
91 } else if (ready == WAIT_OBJECT_0 + nhandles) {
92 /* Windows messages. The MSDN online says that if the application
93 creates a window then its main loop (and we're assuming that
94 it is our SILC Scheduler) must handle the Windows messages, so do
95 it here as the MSDN suggests. */
96 SILC_LOG_DEBUG(("Dispatch window messages"));
97 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
98 TranslateMessage(&msg);
99 DispatchMessage(&msg);
102 /* If timeout is set then we must update the timeout since we won't
103 return and we will give the wait another try. */
104 if (timeo != INFINITE) {
105 timeo -= GetTickCount() - curtime;
106 curtime = GetTickCount();
111 /* Give the wait another try */
114 } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
115 /* Some event occurred. */
116 SILC_LOG_DEBUG(("Dispatch events"));
117 SILC_SCHEDULE_LOCK(schedule);
118 internal->in_schedule = FALSE;
119 return silc_list_count(schedule->fd_dispatch) + 1;
122 internal->in_schedule = FALSE;
126 /* Window callback. We get here when some event occurs on file descriptor
127 or socket that has been scheduled. We add them to dispatch queue and
128 notify the scheduler to handle them. */
130 static LRESULT CALLBACK
131 silc_schedule_wnd_proc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
133 SilcSchedule schedule = (SilcSchedule)GetWindowLongPtr(hwnd, GWL_USERDATA);
134 SilcWin32Scheduler internal;
140 internal = (SilcWin32Scheduler)schedule->internal;
141 fd = (SilcUInt32)wParam;
143 SILC_LOG_DEBUG(("SILC_WM_EVENT fd %d", fd));
144 SILC_SCHEDULE_LOCK(schedule);
146 if (!internal->in_schedule) {
147 /* We are not in scheduler so set up the dispatch queue now */
148 internal->in_schedule = TRUE;
149 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
152 /* Find task by fd */
153 if (!silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
154 NULL, (void *)&task)) {
155 SILC_SCHEDULE_UNLOCK(schedule);
159 /* Ignore the event if the task is not valid anymore */
160 if (!task->header.valid || !task->events) {
161 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 SetWindowLongPtr(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);
332 /* Wakes up the scheduler */
334 void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
337 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
338 ReleaseSemaphore(internal->wakeup_sema, 1, NULL);
339 #endif /* SILC_THREADS */
342 /* Register signal */
344 void silc_schedule_internal_signal_register(SilcSchedule schedule,
347 SilcTaskCallback callback,
348 void *callback_context)
353 /* Unregister signal */
355 void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
362 /* Call all signals */
364 void silc_schedule_internal_signals_call(SilcSchedule schedule,
370 /* Block registered signals in scheduler. */
372 void silc_schedule_internal_signals_block(SilcSchedule schedule,
378 /* Unblock registered signals in schedule. */
380 void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
386 const SilcScheduleOps schedule_ops =
388 silc_schedule_internal_init,
389 silc_schedule_internal_uninit,
391 silc_schedule_internal_schedule_fd,
392 silc_schedule_internal_wakeup,
393 silc_schedule_internal_signal_register,
394 silc_schedule_internal_signal_unregister,
395 silc_schedule_internal_signals_call,
396 silc_schedule_internal_signals_block,
397 silc_schedule_internal_signals_unblock,