5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2008 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.
20 #include "silcruntime.h"
22 const SilcScheduleOps schedule_ops;
24 #define SILC_WM_EVENT WM_USER + 1
27 HWND window; /* Hidden window for receiving socket events */
28 WNDCLASS wclass; /* Window class */
29 HANDLE wakeup_sema; /* Scheduler wakeup semaphore */
30 unsigned int in_schedule : 1;
31 } *SilcWin32Scheduler;
33 /* Our select() call. This simply waits for some events to happen. It also
34 dispatches window messages so it can be used as the main loop of Windows
35 application. This doesn't wait for fds or sockets but does receive
36 notifications via wakeup semaphore when event occurs on some fd or socket.
37 The fds and sockets are scheduled via WSAAsyncSelect. */
39 int silc_select(SilcSchedule schedule, void *context)
41 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
42 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
44 LONG timeo = INFINITE;
49 if (!internal->in_schedule) {
50 internal->in_schedule = TRUE;
51 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
54 /* Add wakeup semaphore to events */
55 handles[nhandles++] = internal->wakeup_sema;
58 if (schedule->has_timeout)
59 timeo = ((schedule->timeout.tv_sec * 1000) +
60 (schedule->timeout.tv_usec / 1000));
62 SILC_SCHEDULE_UNLOCK(schedule);
64 curtime = GetTickCount();
65 ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
68 if (ready == WAIT_FAILED) {
69 /* Wait failed with error */
70 SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
71 SILC_SCHEDULE_LOCK(schedule);
72 internal->in_schedule = FALSE;
75 } else if (ready >= WAIT_ABANDONED_0 &&
76 ready < WAIT_ABANDONED_0 + nhandles) {
77 /* Signal abandoned */
78 SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
79 SILC_SCHEDULE_LOCK(schedule);
80 internal->in_schedule = FALSE;
83 } else if (ready == WAIT_TIMEOUT) {
85 SILC_LOG_DEBUG(("Timeout"));
86 SILC_SCHEDULE_LOCK(schedule);
87 internal->in_schedule = FALSE;
90 } else if (ready == WAIT_OBJECT_0 + nhandles) {
91 /* Windows messages. The MSDN online says that if the application
92 creates a window then its main loop (and we're assuming that
93 it is our SILC Scheduler) must handle the Windows messages, so do
94 it here as the MSDN suggests. */
95 SILC_LOG_DEBUG(("Dispatch window messages"));
96 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
97 TranslateMessage(&msg);
98 DispatchMessage(&msg);
101 /* If timeout is set then we must update the timeout since we won't
102 return and we will give the wait another try. */
103 if (timeo != INFINITE) {
104 timeo -= GetTickCount() - curtime;
105 curtime = GetTickCount();
110 /* Give the wait another try */
113 } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
114 /* Some event occurred. */
115 SILC_LOG_DEBUG(("Dispatch events"));
116 SILC_SCHEDULE_LOCK(schedule);
117 internal->in_schedule = FALSE;
118 return silc_list_count(schedule->fd_dispatch) + 1;
121 internal->in_schedule = FALSE;
125 /* Window callback. We get here when some event occurs on file descriptor
126 or socket that has been scheduled. We add them to dispatch queue and
127 notify the scheduler to handle them. */
129 static LRESULT CALLBACK
130 silc_schedule_wnd_proc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
132 SilcSchedule schedule = (SilcSchedule)GetWindowLongPtr(hwnd, GWL_USERDATA);
133 SilcWin32Scheduler internal;
139 internal = (SilcWin32Scheduler)schedule->internal;
140 fd = (SilcUInt32)wParam;
142 SILC_LOG_DEBUG(("SILC_WM_EVENT fd %d", fd));
143 SILC_SCHEDULE_LOCK(schedule);
145 if (!internal->in_schedule) {
146 /* We are not in scheduler so set up the dispatch queue now */
147 internal->in_schedule = TRUE;
148 silc_list_init(schedule->fd_dispatch, struct SilcTaskStruct, next);
151 /* Find task by fd */
152 if (!silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
153 NULL, (void *)&task)) {
154 SILC_SCHEDULE_UNLOCK(schedule);
158 /* Ignore the event if the task is not valid anymore */
159 if (!task->header.valid || !task->events) {
160 SILC_SCHEDULE_UNLOCK(schedule);
166 switch (WSAGETSELECTEVENT(lParam)) {
169 SILC_LOG_DEBUG(("FD_READ"));
170 task->revents |= SILC_TASK_READ;
171 silc_list_add(schedule->fd_dispatch, task);
175 SILC_LOG_DEBUG(("FD_WRITE"));
176 task->revents |= SILC_TASK_WRITE;
177 silc_list_add(schedule->fd_dispatch, task);
181 SILC_LOG_DEBUG(("FD_ACCEPT"));
182 task->revents |= SILC_TASK_READ;
183 silc_list_add(schedule->fd_dispatch, task);
190 /* Wakeup scheduler */
191 ReleaseSemaphore(internal->wakeup_sema, 1, NULL);
193 SILC_SCHEDULE_UNLOCK(schedule);
201 return DefWindowProc(hwnd, wMsg, wParam, lParam);
206 static SilcBool silc_net_win32_init(void)
208 int ret, sopt = SO_SYNCHRONOUS_NONALERT;
210 WORD ver = MAKEWORD(2, 2);
212 ret = WSAStartup(ver, &wdata);
216 /* Allow using the SOCKET's as file descriptors so that we can poll
217 them with SILC Scheduler. */
218 ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt,
226 /* Uninit Winsock2 */
228 static void silc_net_win32_uninit(void)
233 /* Initializes the platform specific scheduler. This for example initializes
234 the wakeup mechanism of the scheduler. In multi-threaded environment
235 the scheduler needs to be wakenup when tasks are added or removed from
236 the task queues. Returns context to the platform specific scheduler. */
238 void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
240 SilcWin32Scheduler internal;
243 /* Initialize Winsock */
244 silc_net_win32_init();
246 internal = silc_calloc(1, sizeof(*internal));
250 schedule->max_tasks = MAXIMUM_WAIT_OBJECTS;
252 /* Create hidden window. We need window so that we can use WSAAsyncSelect
253 to set socket events. */
254 silc_snprintf(n, sizeof(n), "SilcSchedule-%p", schedule);
255 internal->wclass.lpfnWndProc = silc_schedule_wnd_proc;
256 internal->wclass.cbWndExtra = sizeof(schedule);
257 internal->wclass.lpszClassName = (CHAR *)strdup(n);
258 RegisterClass(&internal->wclass);
259 internal->window = CreateWindow((CHAR *)internal->wclass.lpszClassName, "",
260 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
261 if (!internal->window) {
262 SILC_LOG_ERROR(("Could not create hidden window for scheduler"));
263 DestroyWindow(internal->window);
264 UnregisterClass((CHAR *)n, NULL);
269 /* Set the scheduler as the window's context */
270 SetWindowLongPtr(internal->window, GWL_USERDATA, (void *)schedule);
271 SetWindowPos(internal->window, HWND_BOTTOM, 0, 0, 0, 0, SWP_FRAMECHANGED);
273 internal->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
274 if (!internal->wakeup_sema) {
275 SILC_LOG_ERROR(("Could not create wakeup semaphore for scheduler"));
280 return (void *)internal;
283 /* Uninitializes the platform specific scheduler context. */
285 void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
287 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
293 silc_snprintf(n, sizeof(n), "SilcSchedule-%p", schedule);
294 DestroyWindow(internal->window);
295 UnregisterClass((CHAR *)n, NULL);
297 CloseHandle(internal->wakeup_sema);
298 silc_net_win32_uninit();
303 /* Schedule `task' with events `event_mask'. Zero `event_mask' unschedules. */
305 SilcBool silc_schedule_internal_schedule_fd(SilcSchedule schedule,
308 SilcTaskEvent event_mask)
310 SilcWin32Scheduler internal = (SilcWin32Scheduler)context;
316 SILC_LOG_DEBUG(("Scheduling fd %d for events %d", task->fd, event_mask));
318 if (event_mask & SILC_TASK_READ)
319 events |= FD_READ | FD_ACCEPT | FD_OOB;
320 if (event_mask & SILC_TASK_WRITE)
323 /* Schedule for events. The silc_schedule_wnd_proc will be called to
324 deliver the events for this fd. */
325 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,