Presentation on theme: "Why killing tasklets is good practice"— Presentation transcript:
1Why killing tasklets is good practice Death to tasklets!Why killing tasklets is good practice
2History Multiprocessing Multi-threading Master process spawns off multiple processesWaits for them to finishMulti-threadingWithin a process, threads are spawned„main thread“ waits for their results
3Start Stop A typical problem Main „process“ starts a „server process“ Main process wants to stop server processStartStopGet requestProcess requestFinish request
4How? Ask the server to quit Kill it with death! Message passing is nicePoliteness is niceGood and tidy programing practiceKill it with death!Asynchronous (where does it get killed?)Impolite (will it clean up?)Impossible (what API is that?)
5MEGADEATH Processes: Unix signals: Windows: SIGINT, can cause system calls to fail with an EINTERRUPTED error code.SIGKILL or SIGTERM just kills them outrightFiles are closed, memory is released.Buffers are not flushedWindows:TerminateProcess() kills a process outrightFiles, sockets, are closed, memory released, but buffers are not flushed
6MEGADEATH Threads: pthreads Win32 Python pthread_cancel() on cancellation points.Win32TerminateThread() kills the thread dead.PythonThreading.Thread has no kill() methodPython resources (references) would not be cleaned up by a hard killPthread_cancel() could be used to implement a ‚soft‘ kill but is not available on all platforms.CPython thread switching is synchronized (via GIL) so sending exceptions to threads is technically possibleCPython devs haven‘t drunk the Svali yet.
7MEGADEATH Killing processes/threads: If at all possible, it is messy (portability)C++ exception handling is usually not invokedResources are released, but cleanup not executedSockets are aborted, causing errorsLocks are left un-releasedBuffers are left un-flushedKarma is left un-balanced
8MEGADEATH Tasklets? Tasklet.kill() TaskletExit is BaseException Sends a TaskletExit exception to the taskletImmediatelly awakes the targetOptional „pending“ delivery mode of exceptionTaskletExit is BaseExceptionNot caught by regular exception filters which catch Exception objectsNormally causes the exception to bubble to the topFinally clauses and context managers run as expected
9Microlife Killing tasklets is synchronous It is like sending a message to a blocking methodKilling tasklets is not killingIt is merely raising an exceptionIt can even be caught, logged, handled, etc.Killing tasklet is not a system operationIt invokes the language‘s native exception handling (unlike, e.g. C++)
10MicrolifeNow we have an out-of-band way of sending messages to workers!Code written with proper try/finally scopes works as expected.Other apis and other exceptions can be used:Tasklet.kill(pending=True) # new featureTasklet.throw(exc, val, tb, pending=False)
11Microlife Examples: SocketServer.py def serve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: while not self.__shutdown_request: # XXX: Consider using another file descriptor or # connecting to the socket to wake this up instead of # polling. Polling reduces our responsiveness to a # shutdown request and wastes cpu at all other times. r, w, e = select.select([self], , , poll_interval) if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__is_shut_down.set()
12Microlife Examples: baseCorporation.py while True: self.LogInfo("RunContactUpdates sleeping for", CONTACT_UPDATE_SLEEPTIME, "minutes") blue.pyos.synchro.SleepWallclock(1000 * 60 * CONTACT_UPDATE_SLEEPTIME) if not self.running: return…
13Dust UIUI code can be many tasklets doing things, e.g. Waiting for input.Complex UI state must be shut down e.g. When disconnection occursUI tasklets register with the UI managerUI code carefully uses try:/finally: or context managers to release UI elements.UI manager can kill all registered tasklets, causing UI to be shut down gracefully.
14Dust UI def UIDialogBoxWorker(): with UIManager.register_tasklet(): with UI.DialogueBox() as box:input = box.GetResult() # blocks hereclass UIManager(object):def OnClear(self):for worker in self.GetWorkers():worker.kill()
15Servitor Gated access to a service def StartProcess(self):self.servitor = sake.servitor.DirectServitor()def ServitorCall(self, function, args):""" Wrap the call. This can involve a servitor, for example. Thepurpose of this is to allow Crust to cancel any calls that arein progress."""# Use the DirectServitor. This causes TaskletExit calls to result# in a stacklesslib.errors.CancelledError to be raised.return self.servitor.call(function, args)def Disconnect(self, *reasons):# We have decided to disconnect. Kill all calls currently in actionself.servitor.kill_servitors(args=reasons)
16Servitor Clients is aware of CancelledError (or not): def GetThrused():svc = app.GetService('ThrusingService')try:return svc.DoTheThrus('I am the bethrused. Thrus me!')except stacklesslib.errors.CancelledError:# handle this error here:return None
17Servitor Server wraps its api: class ThrusingService(service): def StartService(self):self.servitor = servitor.SimpleServitor()def GetAPI(self):return servitor.WrapAPI(self)def CancelClients(self):self.servitor.cancel_clients()def DoTheThrus(self, subject):return sys.network.interact(self.target, subject)
18Softkill Tasklet kill is a soft kill A gentle nudge of a rocking boat on a summer eveningRequests a tasklet to politely stop what it is doingTaskletExit is a special exception that is silently ignored at the top level.Like a gentle rustle in the autumn leaves
19Softkill What to be aware of: Every stackless switching function can raise an exception.Running under watchdog, every instruction can raise an exception. (see: atomic)Must be aware of this if writing low-level stackless code, such as when using channels.stacklesslib.locks classes are exception-awareSo are uthread.locks and others in EVE, I believe.
20Softkill Best practices: Use context managers to tear down resources Acquire locks, open files, etc.Know that blocking calls can raise exceptionsBut everyone knew that, right?Use „with stacklesslib.util.atomic():“ to guard critical stackless framework code.Because you just love to write your own synchronization primitives
21Do It! Never again write polling loops with exit request states Grow a moustache or plant an herb garden.Go forth and procreate.