Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyworker.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /*
  2. * Copyright (C) 2019 Weber Yann
  3. *
  4. * This file is part of PyFCGI.
  5. *
  6. * PyFCGI is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * any later version.
  10. *
  11. * PyFCGI is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PyFCGI. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "pyworker.h"
  20. static short _wtimeout;
  21. /**@brief 1 if worker idle else 0 */
  22. static short _worker_idle;
  23. /**@brief Indicate that a worker is idle */
  24. static inline void worker_set_idle();
  25. /**@brief Indicate that a worker is busy */
  26. static inline void worker_set_busy();
  27. /**@brief Process results from a pep333 worker
  28. * @param FCGX_Stream* out stream from libFCGI
  29. * @param PyObject* application function returned value
  30. */
  31. static inline int work333_send_result(FCGX_Stream*, PyObject* ret);
  32. static int worker_piper_sigrcv = 0;
  33. int work333(int wrk_id)
  34. {
  35. PyObject *entry_fun, *pyflush[2], *py_osmod, *entry_ret, *environ,
  36. *start_response, *args;
  37. FCGX_Stream *in_stream, *out_stream, *err_stream;
  38. char **envp;
  39. int count, pipe_out[2], pipe_err[2];
  40. int max_reqs;
  41. struct timeval start, stop;
  42. FCGX_Request *request;
  43. max_reqs = PyFCGI_conf.max_reqs;
  44. pyfcgi_log(LOG_INFO, "Worker started with PEP333 App");
  45. pyinit();
  46. pyfcgi_log(LOG_DEBUG, "Python started");
  47. update_python_fd(pipe_out, pipe_err);
  48. fetch_pyflush(&(pyflush[0]), &(pyflush[1]));
  49. //importing os
  50. py_osmod = python_osmod();
  51. // loading module
  52. entry_fun = import_entrypoint();
  53. pyfcgi_log(LOG_INFO, "Waiting request with %s.%s()",
  54. PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
  55. if(!entry_fun) //but exit if import failed
  56. {
  57. pyfcgi_log(LOG_ALERT, "Unable to import entrypoint");
  58. exit(PYFCGI_FATAL);
  59. }
  60. start_response = get_start_response();
  61. // Initialise FCGI request
  62. request = &(PyFCGI_conf.context.fcgi_request);
  63. if(FCGX_InitRequest(request, PyFCGI_conf.context.fcgi_socket,
  64. FCGI_FAIL_ACCEPT_ON_INTR) == -1)
  65. {
  66. pyfcgi_log(LOG_ALERT, "Unable to init FCGI request");
  67. exit(PYFCGI_FATAL);
  68. }
  69. _worker_idle = 0;
  70. worker_set_idle();
  71. // requests accepting loop
  72. count = 0;
  73. while ((!count || count != max_reqs) &&
  74. FCGX_Accept_r(request) == 0)
  75. {
  76. in_stream = request->in;
  77. out_stream = request->out;
  78. err_stream = request->err;
  79. envp = request->envp;
  80. pyfcgi_wd_arm();
  81. sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
  82. gettimeofday(&start, NULL);
  83. worker_set_busy();
  84. count++;
  85. environ = update_pyenv(py_osmod, envp);
  86. _wtimeout = 0;
  87. libpyfcgi_clean_response();
  88. libpyfcgi.out = out_stream;
  89. libpyfcgi.in = in_stream;
  90. args = Py_BuildValue("OO", environ, start_response);
  91. // Call application function
  92. entry_ret = PyObject_CallObject(entry_fun, args);
  93. if(entry_ret && entry_ret != Py_None )
  94. {
  95. Py_INCREF(entry_ret);
  96. }
  97. if(_wtimeout)
  98. {
  99. PyObject *type, *exc, *tb, *tmp;
  100. // Changing exception to a TimeoutError, but keeping
  101. // the traceback to display
  102. PyErr_Fetch(&type, &exc, &tb);
  103. PyErr_SetString(PyExc_TimeoutError, "Request timeout");
  104. PyErr_Fetch(&type, &exc, &tmp);
  105. PyErr_Restore(type, exc, tb);
  106. log_expt(LOG_ERR);
  107. libpyfcgi_timeout(); // Sending headers if possible
  108. }
  109. if(PyErr_Occurred())
  110. {
  111. pyfcgi_log(LOG_ERR, "PEP333 entrypoint function triggered an exception");
  112. log_expt(LOG_ERR);
  113. }
  114. // able to process returned value
  115. // Simulate python call of libpyfcgi.write_body()
  116. if(entry_ret && entry_ret != Py_None)
  117. {
  118. _pyfcgi_write_body(entry_ret);
  119. if(PyErr_Occurred())
  120. {
  121. log_expt(LOG_ERR);
  122. }
  123. Py_DECREF(entry_ret);
  124. }
  125. // clean stuffs
  126. Py_DECREF(args);
  127. Py_DECREF(environ);
  128. // flush & logs pystdout & pystderr
  129. worker_log_pipes(pipe_out[0], pipe_err[0], pyflush);
  130. FCGX_FClose(out_stream);
  131. FCGX_FClose(in_stream);
  132. FCGX_FClose(err_stream);
  133. FCGX_Finish_r(request);
  134. gettimeofday(&stop, NULL);
  135. stop.tv_sec = stop.tv_sec - start.tv_sec;
  136. stop.tv_usec = stop.tv_usec - start.tv_usec;
  137. if(stop.tv_usec < 0)
  138. {
  139. stop.tv_usec += 1000000;
  140. stop.tv_sec -= 1;
  141. }
  142. pyfcgi_wd_pause();
  143. if(_wtimeout)
  144. {
  145. exit(PYFCGI_TIMEOUT);
  146. }
  147. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d END [OK] %lu bytes in %ld.%06lds",
  148. wrk_id, count, libpyfcgi.rep_sz, stop.tv_sec, stop.tv_usec);
  149. worker_set_idle();
  150. }
  151. worker_set_busy();
  152. return 0;
  153. }
  154. static inline int work333_send_result(FCGX_Stream *out, PyObject* ret)
  155. {
  156. return 0;
  157. }
  158. int work(int wrk_id)
  159. {
  160. PyObject *entry_fun, *pystdout_flush, *pystderr_flush, *py_osmod,
  161. *environ;
  162. FCGX_Stream *in_stream, *out_stream, *err_stream;
  163. char **envp;
  164. int count, pipe_out[2], pipe_err[2], pipe_ctl[2], err, piper_status;
  165. int max_reqs;
  166. struct sigaction act;
  167. struct timeval start, stop;
  168. sigset_t emptyset;
  169. char buf[PIPE_BUF];
  170. size_t rep_sz;
  171. piper_args_t piper_args;
  172. char *piper_stack;
  173. max_reqs = PyFCGI_conf.max_reqs;
  174. piper_args.wrk_id = wrk_id;
  175. piper_args.act = &act;
  176. // preparing sigaction for piper
  177. if(sigemptyset(&emptyset))
  178. {
  179. err = errno;
  180. pyfcgi_log( LOG_ALERT, "sigemptyset fails in piper : %s",
  181. strerror(err));
  182. exit(err);
  183. }
  184. act.sa_handler = worker_piper_sighandler;
  185. act.sa_mask = emptyset;
  186. //act.sa_flags = SA_RESTART;
  187. act.sa_flags = 0;
  188. act.sa_restorer = NULL;
  189. if( !(piper_stack = malloc(PIPER_STACK_SZ)) )
  190. {
  191. err = errno;
  192. pyfcgi_log(LOG_ALERT, "Error while allocating piper stack : %s",
  193. strerror(err));
  194. exit(err);
  195. }
  196. pyfcgi_log(LOG_INFO, "Worker %d started", wrk_id);
  197. update_python_path(); // add cwd to python path
  198. Py_Initialize(); // "start" python
  199. update_python_fd(pipe_out, pipe_err);
  200. piper_args.pystdout = pipe_out[0];
  201. piper_args.pystderr = pipe_err[0];
  202. fetch_pyflush(&pystdout_flush, &pystderr_flush);
  203. pyfcgi_log( LOG_INFO,
  204. "Worker[%d] Python started", wrk_id);
  205. //importing os
  206. py_osmod = python_osmod();
  207. // loading module
  208. entry_fun = import_entrypoint();
  209. pyfcgi_log( LOG_INFO,
  210. "Worker[%d] Waiting request with %s.%s()", wrk_id,
  211. PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
  212. if(!entry_fun) //but exit if import failed
  213. {
  214. pyfcgi_log(LOG_ALERT, "Unable to import entrypoint");
  215. exit(PYFCGI_FATAL);
  216. }
  217. if(pipe(pipe_ctl) == -1)
  218. {
  219. err = errno;
  220. pyfcgi_log(LOG_ALERT,
  221. "Worker[%d] Pipe fails for piper ctl : %s",
  222. wrk_id, strerror(err));
  223. exit(err);
  224. }
  225. piper_args.ctl_pipe = pipe_ctl[1];
  226. _worker_idle = 0;
  227. worker_set_idle();
  228. count = 0;
  229. while ((!count || count != max_reqs) && FCGX_Accept(&in_stream, &out_stream, &err_stream, &envp) >= 0)
  230. {
  231. pyfcgi_wd_arm();
  232. sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
  233. gettimeofday(&start, NULL);
  234. worker_set_busy();
  235. count++;
  236. piper_args.out = out_stream;
  237. //piper_args.req_id = count;
  238. worker_piper_sigrcv = 0;
  239. // if(!PyFCGI_conf.pep333)
  240. /**@todo avoid clone on each request... (using named pipes ?) */
  241. pid_t pid = clone(worker_piper, piper_stack + PIPER_STACK_SZ - 1,
  242. CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | \
  243. SIGCHLD | \
  244. CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_VM,
  245. &piper_args);
  246. if(pid < 0)
  247. {
  248. err = errno;
  249. pyfcgi_log(LOG_ALERT,
  250. "Worker[%d] req #%d Fork failed for piper : %s",
  251. wrk_id, count, strerror(err));
  252. exit(err);
  253. }
  254. environ = update_pyenv(py_osmod, envp);
  255. Py_DECREF(environ);
  256. //close(pipe_ctl[1]);
  257. PyObject_CallObject(entry_fun, NULL);
  258. if(PyErr_Occurred())
  259. {
  260. log_expt(LOG_ERR);
  261. }
  262. else
  263. {
  264. /*
  265. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d funcall [OK]",
  266. wrk_id, count);
  267. */
  268. }
  269. PyObject_CallObject(pystdout_flush, NULL);
  270. PyObject_CallObject(pystderr_flush, NULL);
  271. read(pipe_ctl[0], &buf, 1); // unblock when child ready
  272. FCGX_FClose(in_stream);
  273. FCGX_FClose(err_stream);
  274. //close(pipe_ctl[0]);
  275. kill(pid, WPIPER_SIG); //indicate child python call ended
  276. waitpid(pid, &piper_status, 0);
  277. if(WIFSIGNALED(piper_status))
  278. {
  279. pyfcgi_log(LOG_ERR,
  280. "Woker[%d] req #%d piper terminated by sig %d",
  281. wrk_id, count, WTERMSIG(piper_status));
  282. exit(40);
  283. }
  284. else if(WEXITSTATUS(piper_status))
  285. {
  286. pyfcgi_log(LOG_ERR,
  287. "Woker[%d] req #%d piper exited with error status %d",
  288. wrk_id, count, WEXITSTATUS(piper_status));
  289. exit(41);
  290. }
  291. FCGI_Finish();
  292. if(ctl_get_rep_sz(pipe_ctl[0], &rep_sz))
  293. {
  294. rep_sz = 0;
  295. }
  296. //Increase sem showing the worker is idle
  297. worker_set_idle();
  298. gettimeofday(&stop, NULL);
  299. stop.tv_sec = stop.tv_sec - start.tv_sec;
  300. stop.tv_usec = stop.tv_usec - start.tv_usec;
  301. if(stop.tv_usec < 0)
  302. {
  303. stop.tv_usec += 1000000;
  304. stop.tv_sec -= 1;
  305. }
  306. pyfcgi_wd_pause();
  307. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d END [OK] %lu bytes in %ld.%06lds",
  308. wrk_id, count, rep_sz, stop.tv_sec, stop.tv_usec);
  309. }
  310. worker_set_busy();
  311. free(piper_stack);
  312. Py_Exit(count == max_reqs ?0:EXIT_PYERR);
  313. }
  314. /*
  315. void worker_piper(int wrk_id, int req_id, int pystdout, int pystderr,
  316. int ctl_pipe, FCGX_Stream* out)
  317. */
  318. int worker_piper(void *ptr)
  319. {
  320. piper_args_t *args;
  321. struct pollfd fds[2];
  322. int err, ret, cont, poll_ret;
  323. short revents, closed, nfds;
  324. char buf[PIPE_BUF];
  325. size_t out_sz;
  326. args = ptr;
  327. int wrk_id, pystdout, pystderr, ctl_pipe;
  328. //int req_id;
  329. FCGX_Stream *out;
  330. struct sigaction *act;
  331. wrk_id = args->wrk_id;
  332. //req_id = args->req_id;
  333. pystdout = args->pystdout;
  334. pystderr = args->pystderr;
  335. ctl_pipe = args->ctl_pipe;
  336. out = args->out;
  337. act = args->act;
  338. if(sigaction(WPIPER_SIG, act, NULL))
  339. {
  340. err = errno;
  341. pyfcgi_log( LOG_ALERT, "Unable to sigaction in piper : %s",
  342. strerror(err));
  343. exit(err);
  344. }
  345. write(ctl_pipe, " ", 1);
  346. //pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d piper", wrk_id, req_id);
  347. fds[0].fd = pystderr;
  348. fds[1].fd = pystdout;
  349. fds[0].events = fds[1].events = POLLIN;
  350. fds[0].revents = fds[1].revents = 0;
  351. nfds = 2;
  352. closed = out_sz = 0;
  353. cont = 2;
  354. while(cont)
  355. {
  356. poll_ret = poll(fds, nfds, 0);
  357. //pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d poll_ret = %d", wrk_id, req_id, poll_ret);
  358. if(poll_ret < 0)
  359. {
  360. err = errno;
  361. if(err == EINTR)
  362. {
  363. continue;
  364. }
  365. pyfcgi_log( LOG_WARNING, "Poll error : %s",
  366. strerror(err));
  367. fds[0].revents = fds[1].revents = 0;
  368. continue;
  369. }
  370. if(!poll_ret && worker_piper_sigrcv)
  371. {
  372. cont--;
  373. continue;
  374. }
  375. if(poll_ret && (revents = fds[1].revents))
  376. {
  377. /*
  378. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d STDOUT evt !",
  379. wrk_id, req_id);
  380. */
  381. poll_ret--;
  382. if(revents & POLLIN)
  383. {
  384. /*
  385. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d POLLIN STDOUT !",
  386. wrk_id, req_id);
  387. */
  388. ret = read(pystdout, buf, PIPE_BUF);
  389. /*
  390. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d read(stdout) ret %d",
  391. wrk_id, req_id, ret);
  392. */
  393. if(ret < 0)
  394. {
  395. err = errno;
  396. if(err == EINTR)
  397. {
  398. continue;
  399. }
  400. pyfcgi_log( LOG_ERR,
  401. "Error reading python stdout : %s",
  402. strerror(err));
  403. exit(err);
  404. }
  405. //buf[ret] = '\0';
  406. ret = FCGX_PutStr(buf, ret, out);
  407. out_sz += ret;
  408. if(ret < PIPE_BUF && worker_piper_sigrcv)
  409. {
  410. FCGX_FClose(out);
  411. FCGI_Finish();
  412. write(ctl_pipe, &out_sz, sizeof(size_t));
  413. closed = 1;
  414. nfds = 1;
  415. }
  416. }
  417. //TODO handle other poll events
  418. }
  419. if(poll_ret && (revents = fds[0].revents))
  420. {
  421. /*
  422. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d STDERR evt !",
  423. wrk_id, req_id);
  424. */
  425. if(revents & POLLIN)
  426. {
  427. /*
  428. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d POLLIN STDERR !",
  429. wrk_id, req_id);
  430. */
  431. while(1)
  432. {
  433. ret = read(pystderr, buf, PIPE_BUF);
  434. if(ret < 0)
  435. {
  436. err = errno;
  437. if(err == EINTR)
  438. {
  439. continue;
  440. }
  441. pyfcgi_log( LOG_ERR,
  442. "Error reading python stdout : %s",
  443. strerror(err));
  444. exit(err);
  445. }
  446. buf[ret] = '\0';
  447. pyfcgi_log(LOG_ERR,
  448. "Worker[%d] Python error : %s",
  449. wrk_id, buf);
  450. break;
  451. }
  452. }
  453. //TODO handle other poll events
  454. }
  455. fds[0].revents = fds[1].revents = 0;
  456. }
  457. /*
  458. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d piper exiting...",
  459. wrk_id, req_id);
  460. */
  461. if(!closed)
  462. {
  463. FCGX_FClose(out);
  464. FCGI_Finish();
  465. write(ctl_pipe, &out_sz, sizeof(size_t));
  466. }
  467. return 0;
  468. }
  469. void worker_log_pipes(int pipe_std, int pipe_err, PyObject *flush[2])
  470. {
  471. size_t ret;
  472. char buff[4096];
  473. int i, poll_ret;
  474. int loglevels[2] = {LOG_INFO, LOG_WARNING};
  475. struct pollfd fds[2];
  476. short isflush[2] = {0,0};
  477. char *outnames[2] = {"sys.stdout", "sys.stderr"};
  478. fds[0].fd = pipe_std;
  479. fds[1].fd = pipe_err;
  480. fds[0].events = fds[1].events = POLLIN;
  481. fds[0].revents = fds[1].revents = 0;
  482. for(i=0; i<2; i++)
  483. {
  484. do
  485. {
  486. poll_ret = poll(&(fds[i]), 1, 0);
  487. if(poll_ret)
  488. {
  489. ret = read(pipe_std, buff, 4096);
  490. if(ret == -1)
  491. {
  492. pyfcgi_log(LOG_ERR, "Error reading python %s pipe : %s",
  493. outnames[i], strerror(errno));
  494. exit(PYFCGI_WORKER_FAIL);
  495. }
  496. pyfcgi_log(loglevels[i], "Pyton %s : '%s'",
  497. outnames[i], buff);
  498. }
  499. if(!isflush[i] && (!poll_ret || ret < 4096))
  500. {
  501. isflush[i] = 1;
  502. PyObject_CallObject(flush[i], NULL);
  503. if(PyErr_Occurred())
  504. {
  505. pyfcgi_log(LOG_ERR, "Exception while flushing %s",
  506. outnames[i]);
  507. log_expt(LOG_ERR);
  508. exit(PYFCGI_WORKER_FAIL);
  509. }
  510. }
  511. }while(poll_ret && !isflush[i]);
  512. }
  513. }
  514. void worker_piper_sighandler(int signum)
  515. {
  516. worker_piper_sigrcv = 1;
  517. //pyfcgi_log(LOG_DEBUG, "SIG");
  518. }
  519. int ctl_get_rep_sz(int ctl_pipe, size_t* rep_sz)
  520. {
  521. struct pollfd fds;
  522. int ret, err;
  523. fds.fd = ctl_pipe;
  524. fds.events = POLLIN;
  525. fds.revents = 0;
  526. if( (ret = poll(&fds, 1, 0)) < 0)
  527. {
  528. err = errno;
  529. pyfcgi_log(LOG_ERR, "Failed to poll ctl pipe : %s",
  530. strerror(err));
  531. return -1;
  532. }
  533. if(!ret)
  534. {
  535. pyfcgi_log(LOG_ERR, "No data in ctl pipe for rep_sz...");
  536. return -1;
  537. }
  538. ret = read(ctl_pipe, rep_sz, sizeof(size_t));
  539. if(ret < 0)
  540. {
  541. pyfcgi_log(LOG_ERR, "Error reading ctl pipe : %s",
  542. strerror(errno));
  543. return -1;
  544. }
  545. if(ret < sizeof(size_t))
  546. {
  547. pyfcgi_log(LOG_ERR, "Incomplete read from ctl pipe, no rep_sz...");
  548. return -1;
  549. }
  550. return 0;
  551. }
  552. static void worker_set_idle()
  553. {
  554. int err;
  555. if(_worker_idle) { return; }
  556. if(sem_post(PyFCGI_SEM(SEM_WSTATE).sem) < 0)
  557. {
  558. err = errno;
  559. pyfcgi_log(LOG_ERR, "error incrementing the WSTATE semaphore : %s",
  560. strerror(err));
  561. return;
  562. }
  563. _worker_idle = 1;
  564. }
  565. static void worker_set_busy()
  566. {
  567. int err;
  568. short retry;
  569. struct timespec timeout;
  570. timeout.tv_sec = 0;
  571. timeout.tv_nsec = 100000; //0.0001s
  572. if(!_worker_idle) { return; }
  573. /**@todo The pool handler WILL decrement the sem to figure if the pool
  574. * is busy -__- Using sem_wait make sure that the worker will be able
  575. * to set busy, but it can also freeze if not able to set busy....
  576. * sem_timedwait require to get abstime -_- The better way is
  577. * maybe to nanosleep 0.01s and retry a sem_trywait... SysV sem are
  578. * better T_T
  579. */
  580. err = 0;
  581. retry = 10;
  582. do
  583. {
  584. err = 0;
  585. if(sem_trywait(PyFCGI_SEM(SEM_WSTATE).sem) < 0)
  586. {
  587. err = errno;
  588. if(err == EAGAIN)
  589. {
  590. nanosleep(&timeout, NULL);
  591. err = 1;
  592. }
  593. else
  594. {
  595. pyfcgi_log(LOG_ERR,
  596. "error decrementing the WSTATE semaphore : %s",
  597. strerror(err));
  598. kill(PyFCGI_conf.context.ppid, SIGTERM);
  599. return;
  600. }
  601. }
  602. retry--;
  603. }while(err & retry);
  604. if(err)
  605. {
  606. pyfcgi_log(LOG_ALERT,
  607. "Unable to set busy ! WSTATE sem is allready 0 !!!");
  608. _worker_idle = 0;
  609. kill(PyFCGI_conf.context.ppid, SIGTERM);
  610. kill(PyFCGI_conf.context.pid, SIGTERM);
  611. return;
  612. }
  613. _worker_idle = 0;
  614. }
  615. void worker_sighandler(int signum)
  616. {
  617. pyfcgi_log(LOG_INFO, "%s signal received, exiting...", strsignal(signum));
  618. worker_set_busy();
  619. pyfcgi_IPC_close(IPC_WSTATE | IPC_WREQS);
  620. exit(0);
  621. }
  622. void worker_sigalrmhandler(int signum)
  623. {
  624. _wtimeout = 1;
  625. if(!_worker_idle)
  626. {
  627. // Call PyErr_Interrupt() in order to stop python
  628. PyErr_SetInterrupt();
  629. }
  630. worker_set_busy();
  631. pyfcgi_IPC_close(IPC_WSTATE | IPC_WREQS);
  632. return;
  633. }