21
Death to tasklets! Why killing tasklets is good practice

Death to tasklets! Why killing tasklets is good practice

Embed Size (px)

Citation preview

Page 1: Death to tasklets! Why killing tasklets is good practice

Death to tasklets!

Why killing tasklets is good practice

Page 2: Death to tasklets! Why killing tasklets is good practice

History

• Multiprocessing– Master process spawns off multiple processes– Waits for them to finish

• Multi-threading– Within a process, threads are spawned– „main thread“ waits for their results

Page 3: Death to tasklets! Why killing tasklets is good practice

A typical problem

1. Main „process“ starts a „server process“2. Main process wants to stop server process

Get request

Process request

Finish request

Start

Stop

Page 4: Death to tasklets! Why killing tasklets is good practice

How?

A. Ask the server to quit– Message passing is nice– Politeness is nice– Good and tidy programing practice

B. Kill it with death!– Asynchronous (where does it get killed?)– Impolite (will it clean up?)– Impossible (what API is that?)

Page 5: Death to tasklets! Why killing tasklets is good practice

MEGADEATH

• Processes:– Unix signals:

• SIGINT, can cause system calls to fail with an EINTERRUPTED error code.

• SIGKILL or SIGTERM just kills them outright• Files are closed, memory is released.• Buffers are not flushed

– Windows:• TerminateProcess() kills a process outright• Files, sockets, are closed, memory released, but buffers

are not flushed

Page 6: Death to tasklets! Why killing tasklets is good practice

MEGADEATH

• Threads:– pthreads

• pthread_cancel() on cancellation points.

– Win32• TerminateThread() kills the thread dead.

– Python• Threading.Thread has no kill() method

– Python resources (references) would not be cleaned up by a hard kill– Pthread_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 possible» CPython devs haven‘t drunk the Svali yet.

Page 7: Death to tasklets! Why killing tasklets is good practice

MEGADEATH

• Killing processes/threads:– If at all possible, it is messy (portability)– C++ exception handling is usually not invoked– Resources are released, but cleanup not executed– Sockets are aborted, causing errors– Locks are left un-released– Buffers are left un-flushed– Karma is left un-balanced

Page 8: Death to tasklets! Why killing tasklets is good practice

MEGADEATH

• Tasklets?– Tasklet.kill()

• Sends a TaskletExit exception to the tasklet• Immediatelly awakes the target

– Optional „pending“ delivery mode of exception

– TaskletExit is BaseException• Not caught by regular exception filters which catch

Exception objects• Normally causes the exception to bubble to the top

– Finally clauses and context managers run as expected

Page 9: Death to tasklets! Why killing tasklets is good practice

Microlife

• Killing tasklets is synchronous– It is like sending a message to a blocking method

• Killing tasklets is not killing– It is merely raising an exception• It can even be caught, logged, handled, etc.

• Killing tasklet is not a system operation– It invokes the language‘s native exception handling

(unlike, e.g. C++)

Page 10: Death to tasklets! Why killing tasklets is good practice

Microlife

• Now 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 feature– Tasklet.throw(exc, val, tb, pending=False)

Page 11: Death to tasklets! Why killing tasklets is good practice

Microlife

• 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()

Page 12: Death to tasklets! Why killing tasklets is good practice

Microlife

• 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

Page 13: Death to tasklets! Why killing tasklets is good practice

Dust UI

• UI code can be many tasklets doing things, e.g. Waiting for input.

• Complex UI state must be shut down e.g. When disconnection occurs

• UI tasklets register with the UI manager• UI 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.

Page 14: Death to tasklets! Why killing tasklets is good practice

Dust UI

def UIDialogBoxWorker(): with UIManager.register_tasklet(): with UI.DialogueBox() as box: input = box.GetResult() # blocks here

class UIManager(object): def OnClear(self): for worker in self.GetWorkers(): worker.kill()

Page 15: Death to tasklets! Why killing tasklets is good practice

Servitor

• 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. The purpose of this is to allow Crust to cancel any calls that are in 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 action self.servitor.kill_servitors(args=reasons) 

Page 16: Death to tasklets! Why killing tasklets is good practice

Servitor

• 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

Page 17: Death to tasklets! Why killing tasklets is good practice

Servitor

• 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)

Page 18: Death to tasklets! Why killing tasklets is good practice

Softkill

• Tasklet kill is a soft kill• A gentle nudge of a rocking boat on a summer

evening• Requests a tasklet to politely stop what it is

doing• TaskletExit is a special exception that is silently

ignored at the top level.– Like a gentle rustle in the autumn leaves

Page 19: Death to tasklets! Why killing tasklets is good practice

Softkill

• 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-aware

• So are uthread.locks and others in EVE, I believe.

Page 20: Death to tasklets! Why killing tasklets is good practice

Softkill

• Best practices:– Use context managers to tear down resources• Acquire locks, open files, etc.

– Know that blocking calls can raise exceptions• But 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

Page 21: Death to tasklets! Why killing tasklets is good practice

Do It!

• Never again write polling loops with exit request states

• Grow a moustache or plant an herb garden.• Go forth and procreate.