4 * @brief Implementation of dispatcher_t.
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include "dispatcher.h"
34 #include <utils/linked_list.h>
36 typedef struct private_dispatcher_t private_dispatcher_t
;
39 * private data of the task manager
41 struct private_dispatcher_t
{
59 * number of threads in "threads"
64 * session locking mutex
66 pthread_mutex_t mutex
;
71 linked_list_t
*sessions
;
79 * List of controllers controller_constructor_t
81 linked_list_t
*controllers
;
84 * constructor function to create session context (in constructor_entry_t)
86 context_constructor_t context_constructor
;
89 * user param to context constructor
94 * thread specific initialization handler
96 void (*init
)(void *param
);
99 * argument to pass to thread intiializer
104 * thread specific deinitialization handler
106 void (*deinit
)(void *param
);
109 * param tho thread specific deinitialization handler
115 /** constructor function */
116 controller_constructor_t constructor
;
117 /** parameter to constructor */
119 } constructor_entry_t
;
122 /** session instance */
124 /** condvar to wait for session */
126 /** TRUE if session is in use */
128 /** last use of the session */
133 * create a session and instanciate controllers
135 static session_t
* load_session(private_dispatcher_t
*this)
137 iterator_t
*iterator
;
138 constructor_entry_t
*entry
;
140 context_t
*context
= NULL
;
141 controller_t
*controller
;
143 if (this->context_constructor
)
145 context
= this->context_constructor(this->param
);
147 session
= session_create(context
);
149 iterator
= this->controllers
->create_iterator(this->controllers
, TRUE
);
150 while (iterator
->iterate(iterator
, (void**)&entry
))
152 controller
= entry
->constructor(context
, entry
->param
);
153 session
->add_controller(session
, controller
);
155 iterator
->destroy(iterator
);
161 * create a new session entry
163 static session_entry_t
*session_entry_create(private_dispatcher_t
*this)
165 session_entry_t
*entry
;
167 entry
= malloc_thing(session_entry_t
);
168 entry
->in_use
= FALSE
;
169 pthread_cond_init(&entry
->cond
, NULL
);
170 entry
->session
= load_session(this);
171 entry
->used
= time(NULL
);
176 static void session_entry_destroy(session_entry_t
*entry
)
178 entry
->session
->destroy(entry
->session
);
183 * Implementation of dispatcher_t.add_controller.
185 static void add_controller(private_dispatcher_t
*this,
186 controller_constructor_t constructor
, void *param
)
188 constructor_entry_t
*entry
= malloc_thing(constructor_entry_t
);
190 entry
->constructor
= constructor
;
191 entry
->param
= param
;
192 this->controllers
->insert_last(this->controllers
, entry
);
196 * Actual dispatching code
198 static void dispatch(private_dispatcher_t
*this)
200 FCGX_Request fcgi_req
;
202 if (FCGX_InitRequest(&fcgi_req
, this->fd
, 0) == 0)
207 session_entry_t
*current
, *found
= NULL
;
208 iterator_t
*iterator
;
213 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
214 accepted
= FCGX_Accept_r(&fcgi_req
);
215 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
223 request
= request_create(&fcgi_req
, TRUE
);
228 sid
= request
->get_cookie(request
, "SID");
232 pthread_mutex_lock(&this->mutex
);
233 iterator
= this->sessions
->create_iterator(this->sessions
, TRUE
);
234 while (iterator
->iterate(iterator
, (void**)¤t
))
236 /* check all sessions for timeout */
237 if (!current
->in_use
&&
238 current
->used
< now
- this->timeout
)
240 iterator
->remove(iterator
);
241 session_entry_destroy(current
);
245 streq(current
->session
->get_sid(current
->session
), sid
))
250 iterator
->destroy(iterator
);
253 { /* wait until session is unused */
254 while (found
->in_use
)
256 pthread_cond_wait(&found
->cond
, &this->mutex
);
260 { /* create a new session if not found */
261 found
= session_entry_create(this);
262 this->sessions
->insert_first(this->sessions
, found
);
264 found
->in_use
= TRUE
;
265 pthread_mutex_unlock(&this->mutex
);
267 /* start processing */
268 found
->session
->process(found
->session
, request
);
269 found
->used
= time(NULL
);
271 /* release session */
272 pthread_mutex_lock(&this->mutex
);
273 found
->in_use
= FALSE
;
274 pthread_cond_signal(&found
->cond
);
275 pthread_mutex_unlock(&this->mutex
);
278 request
->destroy(request
);
281 FCGX_FPrintF(fcgi_req.out, "<ul>");
282 char **env = fcgi_req.envp;
285 FCGX_FPrintF(fcgi_req.out, "<li>%s</li>", *env);
288 FCGX_FPrintF(fcgi_req.out, "</ul>");
295 * Setup thread and start dispatching
297 static void start_dispatching(private_dispatcher_t
*this)
299 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
302 this->init(this->init_param
);
306 pthread_cleanup_push(this->deinit
, this->deinit_param
);
308 pthread_cleanup_pop(1);
317 * Implementation of dispatcher_t.run.
319 static void run(private_dispatcher_t
*this, int threads
,
320 void(*init
)(void *param
), void *init_param
,
321 void(*deinit
)(void *param
), void *deinit_param
)
324 this->init_param
= init_param
;
325 this->deinit
= deinit
;
326 this->deinit_param
= deinit_param
;
327 this->thread_count
= threads
;
328 this->threads
= malloc(sizeof(pthread_t
) * threads
);
331 if (pthread_create(&this->threads
[threads
- 1],
332 NULL
, (void*)start_dispatching
, this) == 0)
340 * Implementation of dispatcher_t.waitsignal.
342 static void waitsignal(private_dispatcher_t
*this)
348 sigaddset(&set
, SIGINT
);
349 sigaddset(&set
, SIGTERM
);
350 sigaddset(&set
, SIGHUP
);
351 sigprocmask(SIG_BLOCK
, &set
, NULL
);
356 * Implementation of dispatcher_t.destroy
358 static void destroy(private_dispatcher_t
*this)
360 FCGX_ShutdownPending();
361 while (this->thread_count
--)
363 pthread_cancel(this->threads
[this->thread_count
]);
364 pthread_join(this->threads
[this->thread_count
], NULL
);
366 this->sessions
->destroy_function(this->sessions
, (void*)session_entry_destroy
);
367 this->controllers
->destroy_function(this->controllers
, free
);
374 dispatcher_t
*dispatcher_create(char *socket
, int timeout
,
375 context_constructor_t constructor
, void *param
)
377 private_dispatcher_t
*this = malloc_thing(private_dispatcher_t
);
379 this->public.add_controller
= (void(*)(dispatcher_t
*, controller_constructor_t
, void*))add_controller
;
380 this->public.run
= (void(*)(dispatcher_t
*, int threads
,void(*)(void *),void *,void(*)(void *),void *))run
;
381 this->public.waitsignal
= (void(*)(dispatcher_t
*))waitsignal
;
382 this->public.destroy
= (void(*)(dispatcher_t
*))destroy
;
384 this->sessions
= linked_list_create();
385 this->controllers
= linked_list_create();
386 this->context_constructor
= constructor
;
387 pthread_mutex_init(&this->mutex
, NULL
);
390 this->timeout
= timeout
;
397 this->fd
= FCGX_OpenSocket(socket
, 10);
399 return &this->public;