forked from FOSSEE/scilab-on-cloud
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tornado_main.py
203 lines (158 loc) · 6.08 KB
/
tornado_main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env python
# Run this with
# PYTHONPATH=. DJANGO_SETTINGS_MODULE=testsite.settings
# testsite/tornado_main.py
from tornado.options import options, define, parse_command_line
import django.core.handlers.wsgi
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.wsgi
import os, sys
import json as simplejson
import django
# Gist
import time
import signal
import logging
from functools import partial
MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = 3
#pid = str(os.getpid())
#f = open(os.environ['SOC_PID'], 'w')
#f.write(pid)
#f.close()
django.setup()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "soc.settings")
from concurrent.futures import ThreadPoolExecutor
from tornado import gen
from website.models import (TextbookCompanionExampleDependency,
TextbookCompanionDependencyFiles)
from soc.config import (DEFAULT_TORNADO_WORKERS, DEFAULT_REQUEST_COUNT)
from website.dataentry import entry
from instances import ScilabInstance
import threading
import pwd
from django import db
db.connections.close_all()
from django.db import close_old_connections
define('port', type=int, default=8000)
# Custom settings
from soc.settings import PROJECT_DIR
from django.core.wsgi import get_wsgi_application
# Gist
def sig_handler(server, sig, frame):
io_loop = tornado.ioloop.IOLoop.instance()
def stop_loop(deadline):
now = time.time()
# if now < deadline:
if now < deadline:
logging.info('Waiting for next tick')
io_loop.add_timeout(now + 1, stop_loop, deadline)
else:
io_loop.stop()
logging.info('Shutdown finally')
def shutdown():
logging.info('Stopping Django DB connections...')
from django.db import connections
for conn in connections.all():
conn.close()
logging.info('Stopping http server')
server.stop()
logging.info('Will shutdown in %s seconds ...',
MAX_WAIT_SECONDS_BEFORE_SHUTDOWN)
stop_loop(time.time() + MAX_WAIT_SECONDS_BEFORE_SHUTDOWN)
logging.warning('Caught signal: %s', sig)
io_loop.add_callback_from_signal(shutdown)
# End Gist
def run_as_nobody():
"""Runs the current process as nobody."""
# Set the effective uid and to that of nobody.
nobody = pwd.getpwnam('nobody')
os.setegid(nobody.pw_gid)
os.seteuid(nobody.pw_uid)
# request_count keeps track of the number of requests at hand, it is incremented
# when post method is invoked and decremented before exiting post method in
# class ExecutionHandler.
DEFAULT_WORKERS = DEFAULT_TORNADO_WORKERS
request_count = DEFAULT_REQUEST_COUNT
# ThreadPoolExecutor is an Executor subclass that uses a pool of threads to
# execute
# function calls asynchronously.
# It runs numbers of threads equal to DEFAULT_WORKERS in the background.
executor = ThreadPoolExecutor(max_workers=DEFAULT_WORKERS)
# scilab_executor is an object of class ScilabInstance used to
# manage(spawn, kill)
# the Scilab instances and execute the code using those instances.
scilab_executor = ScilabInstance()
scilab_executor.spawn_instance()
# instance_manager function is run at a fixed interval to kill the
# Scilab instances not in use. If the number of user requests is more than the
# count of active Scilab instances, maximum instances defined will be in
# process. Instances will be killed only when their number is more than the user
# requests.
def instance_manager():
print(scilab_executor.count ,"---", request_count)
if(scilab_executor.count > request_count):
scilab_executor.kill_instances(
scilab_executor.count - request_count - 1)
threading.Timer(300, instance_manager).start()
instance_manager()
# Whenever django server sends an ajax request,
# the request is handled by the ExecutionHandler
# post method passes all the parameters received from the ajax call and
# passes it to the submit method of ThreadPoolExecutor class through its object.
# yield is used to gather the output asynchronously in the variable data
class ExecutionHandler(tornado.web.RequestHandler):
@gen.coroutine
def post(self):
global request_count
request_count += 1
close_old_connections()
token = self.request.arguments['token'][0]
token = token.decode('UTF-8')
code = self.request.arguments['code'][0]
code = code.decode('UTF-8')
book_id = int(self.request.arguments['book_id'][0])
chapter_id = int(self.request.arguments['chapter_id'][0])
example_id = int(self.request.arguments['example_id'][0])
dependency_exists = TextbookCompanionExampleDependency.objects\
.using('scilab').filter(example_id=example_id).exists()
close_old_connections()
#print (example_id)
#print (dependency_exists)
dependency_exists = entry(code, example_id, dependency_exists, book_id)
close_old_connections()
data = yield executor.submit(scilab_executor.execute_code, code, token,
book_id, dependency_exists, chapter_id,
example_id)
close_old_connections()
self.write(data)
request_count -= 1
def main():
parse_command_line()
wsgi_app = tornado.wsgi.WSGIContainer(
get_wsgi_application())
tornado_app = tornado.web.Application(
[
('/execute-code', ExecutionHandler),
('/static/(.*)', tornado.web.StaticFileHandler,
{'path': PROJECT_DIR + '/static/'}),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),
], debug=False)
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
# Gist
# End Gist
try:
#server.start(0)
tornado.ioloop.IOLoop.instance().start()
# signal : CTRL + BREAK on windows or CTRL + C on linux
except KeyboardInterrupt:
signal.signal(signal.SIGTERM, partial(sig_handler, server))
signal.signal(signal.SIGQUIT, partial(sig_handler, server))
sys.exit(0)
# Gist
logging.info("Exit...")
# End Gist
if __name__ == '__main__':
main()