Skip to content

Commit

Permalink
Add support for using daemon threads (fixes #14)
Browse files Browse the repository at this point in the history
  • Loading branch information
jodal committed Jan 12, 2013
1 parent c3971a1 commit f5c1841
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
6 changes: 5 additions & 1 deletion docs/changes.rst
Expand Up @@ -3,7 +3,7 @@ Changes
=======


v1.0.2 (in development)
v1.1.0 (in development)
=======================

- An exception raised in :meth:`pykka.Actor.on_start` didn't stop the actor
Expand All @@ -12,6 +12,10 @@ v1.0.2 (in development)
- Make sure exceptions in :meth:`pykka.Actor.on_stop` and
:meth:`pykka.Actor.on_failure` is logged.

- Add :attr:`pykka.ThreadingActor.use_daemon_thread` flag for optionally
running an actor on a daemon thread, so that it doesn't block the Python
program from exiting. (Fixes: :issue:`14`)


v1.0.1 (2012-12-12)
===================
Expand Down
17 changes: 17 additions & 0 deletions pykka/actor.py
Expand Up @@ -326,6 +326,22 @@ class ThreadingActor(Actor):
threads that are not Pykka actors.
"""

use_daemon_thread = False
"""
A boolean value indicating whether this actor is executed on a thread that
is a daemon thread (:class:`True`) or not (:class:`False`). This must be
set before :meth:`pykka.Actor.start` is called, otherwise
:exc:`RuntimeError` is raised.
The entire Python program exits when no alive non-daemon threads are left.
This means that an actor running on a daemon thread may be interrupted at
any time, and there is no guarantee that cleanup will be done or that
:meth:`pykka.Actor.on_stop` will be called.
Actors do not inherit the daemon flag from the actor that made it. It
always has to be set explicitly for the actor to run on a daemonic thread.
"""

@staticmethod
def _create_actor_inbox():
return _queue.Queue()
Expand All @@ -337,6 +353,7 @@ def _create_future():
def _start_actor_loop(self):
thread = _threading.Thread(target=self._actor_loop)
thread.name = thread.name.replace('Thread', self.__class__.__name__)
thread.daemon = self.use_daemon_thread
thread.start()


Expand Down
19 changes: 19 additions & 0 deletions tests/actor_test.py
Expand Up @@ -298,13 +298,32 @@ class FailingOnFailureActor(FailingOnFailureActor, ThreadingActor):
class SuperInitActor(ThreadingActor):
pass

class DaemonActor(ThreadingActor):
use_daemon_thread = True

def test_actor_thread_is_named_after_pykka_actor_class(self):
alive_threads = threading.enumerate()
alive_thread_names = [t.name for t in alive_threads]
named_correctly = [
name.startswith(AnActor.__name__) for name in alive_thread_names]
self.assert_(any(named_correctly))

def test_actor_thread_is_not_daemonic_by_default(self):
alive_threads = threading.enumerate()
actor_threads = [
t for t in alive_threads if t.name.startswith('AnActor')]
self.assertEqual(1, len(actor_threads))
self.assertFalse(actor_threads[0].daemon)

def test_actor_thread_is_daemonic_if_use_daemon_thread_flag_is_set(self):
actor_ref = self.DaemonActor.start()
alive_threads = threading.enumerate()
actor_threads = [
t for t in alive_threads if t.name.startswith('DaemonActor')]
self.assertEqual(1, len(actor_threads))
self.assertTrue(actor_threads[0].daemon)
actor_ref.stop()


if HAS_GEVENT:
class GeventActorTest(ActorTest, unittest.TestCase):
Expand Down

0 comments on commit f5c1841

Please sign in to comment.