[Python-Dev] Sandboxed Threads in Python
Phillip J. Eby
pje at telecommunity.com
Sat Oct 8 02:51:41 CEST 2005
At 06:12 PM 10/7/2005 -0600, Adam Olsen wrote:
>Okay, basic principal first. You start with a sandboxed thread that
>has access to nothing. No modules, no builtins, *nothing*. This
>means it can run without the GIL but it can't do any work.
It sure can't. You need at least the threadstate and a builtins dictionary
to do any work.
> To make it
>do something useful we need to give it two things: first, immutable
>types that can be safely accessed without locks,
This is harder than it sounds. Integers, for example, have a custom
allocator and a free list, not to mention a small-integer cache. You would
somehow need to duplicate all that for each sandbox, or else you have to
make those integers immortal using your "magic constant".
>Turns out it's quite easy and it doesn't harm performance of existing
>code or require modification (but a recompile is necessary). The idea
>is to only use a cyclic garbage collector for cleaning them up,
Um, no, actually. You need a mark-and-sweep GC or something of that
ilk. Python's GC only works with objects that *have refcounts*, and it
works by clearing objects that are in cycles. The clearing causes
DECREF-ing, which then causes objects to be freed. If you have objects
without refcounts, they would be immortal and utterly unrecoverable.
>means we need to disable the reference counting. That requires we
>modify Py_INCREF and Py_DECREF to be a no-op if ob_refcnt is set to a
>magic constant (probably a negative value).
And any object with the magic refcount will live *forever*, unless you
manually deallocate it.
>That's all it takes. Modify Py_INCREF and Py_DECREFs to check for a
>magic constant. Ahh, but the performance? See for yourself.
First, you need to implement a garbage collection scheme that can deal with
not having refcounts. Otherwise you're not comparing apples to apples
here, and your programs will leak like crazy.
Note that implementing a root-based GC for Python is non-trivial, since
extension modules can store pointers to PyObjects anywhere they
like. Further, many Python objects don't even support being tracked by the
current cycle collector.
So, changing this would probably require a lot of C extensions to be
rewritten to support the needed API changes for the new garbage collection
>So to sum up, by prohibiting mutable objects from being transferred
>between sandboxes we can achieve scalability on multiple CPUs, making
>threaded programming easier and more reliable, as a bonus get secure
>sandboxes, and do that all while maintaining single-threaded
>performance and requiring minimal changes to existing C modules
Unfortunately, you have only succeeded in restating the problem, not
reducing its complexity. :) In fact, you may have increased the
complexity, since now you need a threadsafe garbage collector, too.
Oh, and don't forget - newstyle classes keep weak references to all their
subclasses, which means for example that every time you subclass 'dict',
you're modifying the "immutable" 'dict' class. So, unless you recreate all
the classes in each sandbox, you're back to needing locking. And if you
recreate everything in each sandbox, well, I think you've just reinvented
More information about the Python-Dev