Klaus Demo bjoern / 573518b
Fix #77: Don't abort running requests on ^C The graceful shutdown is achieved by stopping the accept and signal watchers. This way, we're not accepting any more clients and the main loop ends as soon as all currently processed requests have been worked off. This patch also ports the libev API calls from libev v3 to v4. Jonas Haag 5 years ago
2 changed file(s) with 62 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
1616 #include "wsgi.h"
1717 #include "server.h"
1818
19 #define SERVER_INFO(loop) ((ServerInfo*)ev_userdata(loop))
2019 #define READ_BUFFER_SIZE 64*1024
2120 #define Py_XCLEAR(obj) do { if(obj) { Py_DECREF(obj); obj = NULL; } } while(0)
2221 #define GIL_LOCK(n) PyGILState_STATE _gilstate_##n = PyGILState_Ensure()
3433 done,
3534 aborted,
3635 };
36
37 typedef struct {
38 ServerInfo* server_info;
39 ev_io accept_watcher;
40 } ThreadInfo;
3741
3842 typedef void ev_io_callback(struct ev_loop*, ev_io*, const int);
3943
5559 void server_run(ServerInfo* server_info)
5660 {
5761 struct ev_loop* mainloop = ev_loop_new(0);
58 ev_set_userdata(mainloop, server_info);
59
60 ev_io accept_watcher;
61 ev_io_init(&accept_watcher, ev_io_on_request, server_info->sockfd, EV_READ);
62 ev_io_start(mainloop, &accept_watcher);
62
63 ThreadInfo thread_info;
64 thread_info.server_info = server_info;
65 ev_set_userdata(mainloop, &thread_info);
66
67 ev_io_init(&thread_info.accept_watcher, ev_io_on_request, server_info->sockfd, EV_READ);
68 ev_io_start(mainloop, &thread_info.accept_watcher);
6369
6470 #if WANT_SIGINT_HANDLING
6571 ev_signal signal_watcher;
6975
7076 /* This is the program main loop */
7177 Py_BEGIN_ALLOW_THREADS
72 ev_loop(mainloop, 0);
73 ev_default_destroy();
78 ev_run(mainloop, 0);
79 ev_loop_destroy(mainloop);
7480 Py_END_ALLOW_THREADS
7581 }
7682
7783 #if WANT_SIGINT_HANDLING
7884 static void
85 pyerr_set_interrupt(struct ev_loop* mainloop, struct ev_cleanup* watcher, const int events)
86 {
87 PyErr_SetInterrupt();
88 free(watcher);
89 }
90
91 static void
7992 ev_signal_on_sigint(struct ev_loop* mainloop, ev_signal* watcher, const int events)
8093 {
8194 /* Clean up and shut down this thread.
8295 * (Shuts down the Python interpreter if this is the main thread) */
83 ev_unloop(mainloop, EVUNLOOP_ALL);
84 PyErr_SetInterrupt();
96 ev_cleanup* cleanup_watcher = malloc(sizeof(ev_cleanup));
97 ev_cleanup_init(cleanup_watcher, pyerr_set_interrupt);
98 ev_cleanup_start(mainloop, cleanup_watcher);
99
100 ev_io_stop(mainloop, &((ThreadInfo*)ev_userdata(mainloop))->accept_watcher);
101 ev_signal_stop(mainloop, watcher);
85102 }
86103 #endif
87104
106123 }
107124
108125 Request* request = Request_new(
109 SERVER_INFO(mainloop),
126 ((ThreadInfo*)ev_userdata(mainloop))->server_info,
110127 client_fd,
111128 inet_ntoa(sockaddr.sin_addr)
112129 );
0 import time
1 import threading
2 import bjoern
3 import os
4 import signal
5 import httplib
6
7 HOST = ('127.0.0.1', 9000)
8
9 request_completed = False
10
11 def application(environ, start_response):
12 start_response('200 ok', [])
13 yield "chunk1"
14 os.kill(os.getpid(), signal.SIGINT)
15 yield "chunk2"
16 yield "chunk3"
17 global request_completed
18 request_completed = True
19
20
21 def requester():
22 conn = httplib.HTTPConnection(*HOST)
23 conn.request("GET", "/")
24 conn.getresponse()
25
26
27 threading.Thread(target=requester).start()
28 try:
29 bjoern.run(application, *HOST)
30 except KeyboardInterrupt:
31 assert request_completed
32 else:
33 raise RuntimeError("Didn't receive KeyboardInterrupt")