So, I've been thinking about multithreading in MOO (particularly with
Stunt) and have wondered about implementing a threading and mutex API.
Something similar to the thread support library in C++
(https://en.cppreference.com/w/cpp/thread), but not quite. So to
create a thread you might call:
id = thread_create(obj object, str verb, list args);
And for a mutex:
"id holds the mutex number. Mutexes are globally shared across the
entire server.";
id = mutex_create();
To lock the mutex you'd call:
mutex_lock(int mutex);
or
mutex_try_lock(int mutex);
Where mutex is the number of the mutex given by mutex_create(), and
force_lock uses lock() instead of try_lock(). mutex_lock() blocks
until the lock succeeds. (I'm curious if I should even add that; it
would be very easy to hang the MOO, and it would be incredibly easy to
cause deadlocks with that.) mutex_try_lock() returns immediately if
the lock fails.
So, here are the functions I'm thinking about adding:
Thread control:
thread_create(object, verb, args) -- creates and starts a new thread
by calling object:verb(args)
thread_destroy(thread) -- destroys thread and terminates it
thread_join(thread) - waits for a thread to finish execution, blocking
the main thread if needed.
thread_detach(thread) - permits thread to execute independently of the
master thread
thread_joinable(thread) - determines if thread is joinable.
thread_hardware_concurrency() - returns max number of concurrent threads
yield() - suggests that the implementation reschedule execution of
threads (from the above linked page)
Mutex control:
mutex_create() - returns a mutex ID, which is used in the following functions
mutex_lock(mutex) - locks the mutex, blocks if the mutex is not
available (my above concerns apply)
mutex_try_lock(mutex) - tries to lock the mutex, returns if the mutex
is not available
mutex_unlock(mutex) - unlocks the mutex
mutex_destroy(mutex) - destroys all references to the mutex and allows
the system to reclaim resources for it.
Those are all the functions I want to implement for now, I don't want
to implement too much in one go.
--
You received this message because you are subscribed to the Google Groups "MOO Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to MOO-talk+u...@googlegroups.com.
To post to this group, send email to MOO-...@googlegroups.com.
Visit this group at https://groups.google.com/group/MOO-talk.
For more options, visit https://groups.google.com/d/optout.
>> email to MOO-talk+unsubscribe@googlegroups.com.
>> email to MOO-talk+u...@googlegroups.com.
On Fri, Nov 2, 2018 at 5:39 PM Ethin Probst <harlyd...@gmail.com> wrote:I like your lock algorithm. For the lock maintenance, could an std::unordered_map<int, <lock_type>> work? That would make it easy for verbs to access the map if they need to forcefully unlock one of them (or create new locks).
I'm struggling to see where a map would be useful and why a verb would be forcefully unlocking anything. Premature unlocking seems likely to be a recipe for disaster. Bear in mind that locks will only be held while the task is actually running, so for $server_options.(bg/fg)_(ticks/seconds) at most. Once the task suspends all the locks will be released. I also can't think of any uses for accessing locks where you wouldn't just fetch the lock from the object again.
Or even just an std::vector<mutex>. That would be even better; then you just do something like this to unlock all the locks: std::for_each(locks.begin(), locks.end(), [&](std::mutex lock) { lock.unlock(); }); Plus, that final algorithm has far more advantages than disadvantages, and would probably be easier to implement.
I don't do C++ so I'm not familiar with what it offers, but I agree that a simple list of locks to be released is probably the better option. As I mentioned before I can't think of any uses for a map of locks. One thing that has just occurred to me is that whatever lock implementation you choose needs a way of *upgrading* from a shared lock to an exclusive one--otherwise a task would always deadlock itself when writing to a property it had previously read!
It wouldn't be overly complicated to make notify()/read() run in their own thread now; the issue is actually (for read()) taking control from the interpreter and passing it to the thread, then returning it when the thread terminates.
1. Rewriting the parser to go through LLVM and making the vm be obsolete. With LLVM you get JIT which would provide presumably a decent performance boost.
3) Finding a better non flat-file way to store MOO objects. I'm invisioning something that moves us closer to the concept of transaction writing, which would move us closer to threads.
boost::lock_guard<Object> guard(*this);
Here is an extremely basic example of a "classified" Object, to give you an idea of what would need to be done. This is the bare minimum and not enough, but gives you an idea:
class Object: public basic_lockable_adapter<mutex> { private: Objid _id; Objid _owner; const char *_name; int _flags; /* see db.h for `flags' values */ Var _location; Var _contents; Var _parents; Var _children; Pval *_propval; unsigned int _nval; Verbdef *_verbdefs; Proplist _propdefs; /* The nonce marks changes to the propval layout caused by changes * to parentage, property addition/deletion, etc. Every value is * globally unique. */ unsigned int _nonce; public: Objid id() const {return _id;} void id(const Objid var) {boost::lock_guard<Object> guard(*this); _id = var;} Objid owner() const {return _owner;} void owner(const Objid var) {boost::lock_guard<Object> guard(*this); _owner = var;} const char *name() const {return _name;} void name(const char *var) {boost::lock_guard<Object> guard(*this); _name = var;} int flags() const {return _flags;} void flags(const int var) {boost::lock_guard<Object> guard(*this); _flags = var;} Var location() const {return _location;} void location(const Var var) {boost::lock_guard<Object> guard(*this); _location = var;} Var contents() const {return _contents;} void contents(const Var var) {boost::lock_guard<Object> guard(*this); _contents = var;} Var parents() const {return _parents;} void parents(const Var var) {boost::lock_guard<Object> guard(*this); _parents = var;} Var children() const {return _children;} void children(const Var var) {boost::lock_guard<Object> guard(*this); _children = var;} Pval* propval() const {return _propval;} void propval(const Pval* var) {boost::lock_guard<Object> guard(*this); _propval = var;} unsigned int nval() const {return _nval;} void nval(const unsigned int var) {boost::lock_guard<Object> guard(*this); _nval = var;} Verbdef* verbdefs() const {return _verbdefs;} void verbdefs(const Verbdef* var) {boost::lock_guard<Object> guard(*this); _verbdefs = var;} Proplist propdefs() const {return _propdefs;} void propdefs(const Proplist var) {boost::lock_guard<Object> guard(*this); _propdefs = var;} unsigned int nonce() const {return _nonce;} void nonce(const unsigned int var) {boost::lock_guard<Object> guard(*this); _nonce = var;} } Object;