-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathemultimer.py
executable file
·225 lines (168 loc) · 4.98 KB
/
emultimer.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
"""
Author: Justin Cappos
Start Date: 29 June 2008
Description:
Timer functions for the sandbox. This does sleep as well as setting and
cancelling timers.
"""
import threading
import thread # Armon: this is to catch thread.error
import time
import restrictions
import nanny
import idhelper
# This is to use do_sleep
import misc
# for printing exceptions
import tracebackrepy
# for harshexit
import harshexit
timerinfo = {}
# Table of timer structures:
# {'timer':timerobj,'function':function}
# Armon: Prefix for use with event handles
EVENT_PREFIX = "_EVENT:"
# Generates a valid event handle
def generate_eventhandle():
"""
<Purpose>
Generates a string event handle that can be used to uniquely identify an event.
It is formatted so that cursory verification can be performed.
<Returns>
A string event handle.
"""
# Get a unique handle from idhelper
uniqueh = idhelper.getuniqueid()
# Return the unique handle prefixed with EVENT_PREFIX
return (EVENT_PREFIX + uniqueh)
# Helps validate an event handle
def is_valid_eventhandle(eventhandle):
"""
<Purpose>
Determines if a given event handle is valid.
This does not guarantee validity, just proper form.
<Arguments>
eventhandle:
The event handle to be checked.
<Returns>
True if valid, False otherwise.
"""
# The handle must be a string, check type first
if type(eventhandle) != str:
return False
# Check if the handle has the correct prefix
return eventhandle.startswith(EVENT_PREFIX)
# Public interface!
def sleep(seconds):
"""
<Purpose>
Allow the current event to pause execution (similar to time.sleep()).
This function will not return early for any reason
<Arguments>
seconds:
The number of seconds to sleep. This can be a floating point value
<Exceptions>
None.
<Side Effects>
None.
<Returns>
None.
"""
restrictions.assertisallowed('sleep',seconds)
# Use the do_sleep implementation in misc
misc.do_sleep(seconds)
# Public interface!
def settimer(waittime, function, args):
"""
<Purpose>
Allow the current event to set an event to be performed in the future.
This does not guarantee the event will be triggered at that time, only
that it will be triggered after that time.
<Arguments>
waittime:
The minimum amount of time to wait before delivering the event
function:
The function to call
args:
The arguments to pass to the function. This should be a tuple or
list
<Exceptions>
None.
<Side Effects>
None.
<Returns>
A timer handle, for use with canceltimer
"""
restrictions.assertisallowed('settimer',waittime)
eventhandle = generate_eventhandle()
nanny.tattle_add_item('events',eventhandle)
tobj = threading.Timer(waittime,functionwrapper,[function] + [eventhandle] + [args])
# Set the name of the thread
tobj.setName(idhelper.get_new_thread_name(EVENT_PREFIX))
timerinfo[eventhandle] = {'timer':tobj}
# Check if we get an exception trying to create a new thread
try:
# start the timer
tobj.start()
except thread.error, exp:
# Set exit code 56, which stands for a Threading Error
# The Node manager will detect this and handle it
harshexit.harshexit(56)
return eventhandle
# Private function. This exists to allow me to do quota based items
def functionwrapper(func, timerhandle, args):
#restrictions ?
# call the function with the arguments
try:
if timerhandle in timerinfo:
del timerinfo[timerhandle]
else:
# I've been "stopped" by canceltimer
return
except KeyError:
# I've been "stopped" by canceltimer
return
try:
func(*args)
except:
# Exit if they throw an uncaught exception
tracebackrepy.handle_exception()
harshexit.harshexit(30)
# remove the event before I exit
nanny.tattle_remove_item('events',timerhandle)
# Public interface!
def canceltimer(timerhandle):
"""
<Purpose>
Cancels a timer.
<Arguments>
timerhandle:
The handle of the timer that should be stopped. Handles are
returned by settimer
<Exceptions>
None.
<Side Effects>
None.
<Returns>
If False is returned, the timer already fired or was cancelled
previously. If True is returned, the timer was cancelled
"""
restrictions.assertisallowed('canceltimer')
# Armon: Check that the given handle is valid
if not is_valid_eventhandle(timerhandle):
raise Exception("Invalid timer handle specified!")
try:
timerinfo[timerhandle]['timer'].cancel()
except KeyError:
# The timer already fired (or was cancelled)
return False
try:
del timerinfo[timerhandle]
except KeyError:
# The timer just fired (or was cancelled)
return False
else:
# I was able to delete the entry, the function will abort. I can remove
# the event
nanny.tattle_remove_item('events',timerhandle)
return True