I've never understood benefit of hiding mutexes like that Java construct does. It even does so in three different ways depending on how it's used. Is the code somehow made more clear because the mutexes disappear? I prefer to see them, so I can tell easily which regions are synchronized with each other. That also makes it easier to search on the mutex variable to find those regions.
There's one benefit with a block construct like that: It makes the region where the lock is held more clear. (The pike way of relying on refcount garbing of the lock has lead to many bugs, e.g. with the tail recursion optimization. I don't rely on refcounting for locks anymore; I always zero them explicitly afterwards.)
So in my view a good synchronized construct would always require a mutex argument. E.g:
Thread.Mutex my_mutex = Thread.Mutex(); synchronized(my_mutex) void my_first_function() {...}
void my_second_function() { ... synchronized(my_mutex) { ... } }
The second case above is solvable with implicit lambda, but I'd rather avoid implementing it that way since it "destroys" the syntax so it can't be upgraded to a better implementation later on (see the blurb about implicit lambdas in the CHANGES file).
As a transitionary measure, we could perhaps have something like this instead that works through implicit lambdas:
my_mutex->synchronize() { ... };
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-04 19:14: Subject: synchronized
One thing that's nice in Java is the 'synchronized keyword. It would be nice to have that functionality in Pike since it's easier than manually using mutex locks (nicer code):
synchronized void do_something() { ... }
and
mapping foo; void do_something() { synchronized(foo) { work on foo } }
and
void do_something() { ... synchronized { locked region } ... }
would be nice. The first one creates an implicit mutex lock for the entire method, the second one creates a lock bound to a specific variable (i.e you can have multiple methods "locking" the same variable) and the third method works just like the first, except for a specific region rather than the entire method.
Another possibility is to have synchronized classes:
synchronized class { ... }
and variable instanses:
synchronized mapping foo = ([]);
But the first three versions are IMHO more interesting.
How hard would this be to implement in pike? I'm guessing "quite hard", but... :)
/ David Hedbor
The benefit is that it's very clean and amazingly clear what it does (I don't understand your "so I can tell easily which regions are synchronized with each other" comment. What is hard?
The synchronized method format makes it so that the format is never accessed by more than one thread at any time. Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
That said a implicit lambda construct I wrote earlier that uses a weak mapping can check to see if the variable sent to it is a mutex and if is use that rather than another entry in the weak mapping.
/ David Hedbor
Previous text:
2003-02-05 01:00: Subject: synchronized
I've never understood benefit of hiding mutexes like that Java construct does. It even does so in three different ways depending on how it's used. Is the code somehow made more clear because the mutexes disappear? I prefer to see them, so I can tell easily which regions are synchronized with each other. That also makes it easier to search on the mutex variable to find those regions.
There's one benefit with a block construct like that: It makes the region where the lock is held more clear. (The pike way of relying on refcount garbing of the lock has lead to many bugs, e.g. with the tail recursion optimization. I don't rely on refcounting for locks anymore; I always zero them explicitly afterwards.)
So in my view a good synchronized construct would always require a mutex argument. E.g:
Thread.Mutex my_mutex = Thread.Mutex(); synchronized(my_mutex) void my_first_function() {...}
void my_second_function() { ... synchronized(my_mutex) { ... } }
The second case above is solvable with implicit lambda, but I'd rather avoid implementing it that way since it "destroys" the syntax so it can't be upgraded to a better implementation later on (see the blurb about implicit lambdas in the CHANGES file).
As a transitionary measure, we could perhaps have something like this instead that works through implicit lambdas:
my_mutex->synchronize() { ... };
/ Martin Stjernholm, Roxen IS
I think Martin wants a solution for the problem
a->lock() { // Some code };
b->lock() { // Some other code };
a->lock() { // Even more code };
While following is possible, it's a dull sword:
void kludge(int(0..1) x) { synchronized { if(x) // Some code else // Even more code } }
kludge(0); synchronized { // Some other code } kludge(1);
/ Martin Nilsson (Åskblod)
Previous text:
2003-02-05 02:20: Subject: synchronized
The benefit is that it's very clean and amazingly clear what it does (I don't understand your "so I can tell easily which regions are synchronized with each other" comment. What is hard?
The synchronized method format makes it so that the format is never accessed by more than one thread at any time. Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
That said a implicit lambda construct I wrote earlier that uses a weak mapping can check to see if the variable sent to it is a mutex and if is use that rather than another entry in the weak mapping.
/ David Hedbor
I asked how it makes the code more clear, and you responded with that it makes it amazingly clear. Not much of a motivation. I gave at least one concrete case where it isn't: Since there's no mutex name it gets harder to do a grep or similar to find all regions that are synchronized with each other.
Granted, in the case where synchronized is used with a variable the link is fairly explicit although the mutex itself still is hidden.
The synchronized method format makes it so that the format is never accessed by more than one thread at any time.
Yes, but I don't think it helps any that the scope of synchronization is implicit. It becomes easier to just use it with no clear understanding about what other methods it also gets synchronized with. And in my experience it's absolutely essential to have exactly the right scope for each mutex; using the same one for too many functions is just as disastrous as using it with too few.
Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
Yes, as long as that else is consciously synchronizing on it; it's still just as easy to forget the synchronize block for the variable somewhere and thus get races on it. I think _that_ is a problem worth solving, not just to get a mutex implicitly declared next to the variable.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", and the compiler would then do all necessary flow analysis and give me errors whenever I broke it. That would be a very useful thing.
Also note "variables"; in almost every case I've had to protect more than one variable with the same mutex. To use the Java synchronized construct for that I'd have to encapsulate them in a small class of their own, which makes it a bit clumsy and incurs an extra unnecessary indirection.
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
If you read my example carefully you'll see I didn't talk about that but rather "synchronized (mutex) {...}".
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
And I see it as a clear feature to have the mutexes explicit. They are important things. They must get absolutely right for everything to work well. They have global implications that can cause unrelated parts of the program to interact in unexpected ways (which is the common cause for deadlocks). I.e. they really require good attention. Making them implicit seems to me like trying to sweep the problem under the mat.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 02:20: Subject: synchronized
The benefit is that it's very clean and amazingly clear what it does (I don't understand your "so I can tell easily which regions are synchronized with each other" comment. What is hard?
The synchronized method format makes it so that the format is never accessed by more than one thread at any time. Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
That said a implicit lambda construct I wrote earlier that uses a weak mapping can check to see if the variable sent to it is a mutex and if is use that rather than another entry in the weak mapping.
/ David Hedbor
I asked how it makes the code more clear, and you responded with that it makes it amazingly clear. Not much of a motivation. I gave at least one concrete case where it isn't: Since there's no mutex name it gets harder to do a grep or similar to find all regions that are synchronized with each other.
Why s this: object lck = my_mutex->lock(); ... destruct(lck)
so much easier to grasp than:
syncronized(mappingfoo) { }
Granted, in the case where synchronized is used with a variable the link is fairly explicit although the mutex itself still is hidden.
Exactly and that is the point - hide the unnecessarily visible code to do the locking. Also when you synchronize a method, it is, as I said in the message you commented, extremely obvious how it works - every call to the method uses the same implicit mutex lock.
I still don't understand what is so unclear / hard about it.
Yes, but I don't think it helps any that the scope of synchronization is implicit. It becomes easier to just use it with no clear understanding about what other methods it also gets synchronized with.
A synchronized methods doesn't get get synchronized with another method at all. I don't understand what you're talking about here.
And in my experience it's absolutely essential to have exactly the right scope for each mutex; using the same one for too many functions is just as disastrous as using it with too few.
Which is exactly why the java versions work so well - each synchronized method has their own mutex (obviously). The same goes for each synchronize-statement that is based on a variable.
Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
Yes, as long as that else is consciously synchronizing on it; it's still just as easy to forget the synchronize block for the variable somewhere and thus get races on it. I think _that_ is a problem worth solving, not just to get a mutex implicitly declared next to the variable.
Of course. It's not meant to solve any programmatical error - it's to make the syntax easier / nicer. I.e saving lines of code an global variables, not improving the code or making it easier not to make mistakes.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", and the compiler would then do all necessary flow analysis and give me errors whenever I broke it. That would be a very useful thing.
Which was one of the suggested additional options I had:
synchronized mapping foo = ([]) ...
Also note "variables"; in almost every case I've had to protect more than one variable with the same mutex. To use the Java synchronized construct for that I'd have to encapsulate them in a small class of their own, which makes it a bit clumsy and incurs an extra unnecessary indirection.
Then isn't it beautiful that you don't HAVE to use that syntax? Or why not use it with your explicit mutex variable:
synchronized(my_mutex) { modify var 1 and 2 }
If you read my example carefully you'll see I didn't talk about that but rather "synchronized (mutex) {...}".
Well, it's exactly the same thing, code-wise (except that if you already have a mutex variable one obviously doesn't need to create and store on in a mapping).
And I see it as a clear feature to have the mutexes explicit. They are important things. They must get absolutely right for everything to work well. They have global implications that can cause unrelated parts of the program to interact in unexpected ways (which is the common cause for deadlocks). I.e. they really require good attention. Making them implicit seems to me like trying to sweep the problem under the mat.
I disagree strongly. The synchronized keyword doesn't make anything less "safe", it simply makes it easier when you need some simple mutex locking.
You simply don't like the concept of not seeing the mutex lock and the explicit use of it. That is an ok opinion and you can continue with that belief. Personally I see this feature as a coding simplification, in many ways similar to implicit lambdas.
/ David Hedbor
Previous text:
2003-02-05 04:05: Subject: synchronized
I asked how it makes the code more clear, and you responded with that it makes it amazingly clear. Not much of a motivation. I gave at least one concrete case where it isn't: Since there's no mutex name it gets harder to do a grep or similar to find all regions that are synchronized with each other.
Granted, in the case where synchronized is used with a variable the link is fairly explicit although the mutex itself still is hidden.
The synchronized method format makes it so that the format is never accessed by more than one thread at any time.
Yes, but I don't think it helps any that the scope of synchronization is implicit. It becomes easier to just use it with no clear understanding about what other methods it also gets synchronized with. And in my experience it's absolutely essential to have exactly the right scope for each mutex; using the same one for too many functions is just as disastrous as using it with too few.
Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
Yes, as long as that else is consciously synchronizing on it; it's still just as easy to forget the synchronize block for the variable somewhere and thus get races on it. I think _that_ is a problem worth solving, not just to get a mutex implicitly declared next to the variable.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", and the compiler would then do all necessary flow analysis and give me errors whenever I broke it. That would be a very useful thing.
Also note "variables"; in almost every case I've had to protect more than one variable with the same mutex. To use the Java synchronized construct for that I'd have to encapsulate them in a small class of their own, which makes it a bit clumsy and incurs an extra unnecessary indirection.
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
If you read my example carefully you'll see I didn't talk about that but rather "synchronized (mutex) {...}".
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
And I see it as a clear feature to have the mutexes explicit. They are important things. They must get absolutely right for everything to work well. They have global implications that can cause unrelated parts of the program to interact in unexpected ways (which is the common cause for deadlocks). I.e. they really require good attention. Making them implicit seems to me like trying to sweep the problem under the mat.
/ Martin Stjernholm, Roxen IS
Why s this: object lck = my_mutex->lock(); ... destruct(lck)
so much easier to grasp than:
syncronized(mappingfoo) { }
It's not. Please read the second paragraph of 9697233.
A synchronized methods doesn't get get synchronized with another method at all. I don't understand what you're talking about here.
I'm talking about how synchronized works in Java, since I gathered that's where you got the construct from. But ok, then it isn't a problem but otoh it makes the construct fairly useless; I've yet to see a single case where only one individual function needs to be protected by a mutex.
Which is exactly why the java versions work so well - each synchronized method has their own mutex (obviously).
Java doesn't work like that; please read 8.4.3.6 in the Java spec. Since you say they work so well I assume it wouldn't be a problem for you to give a natural example where that synchronization works?
It's not meant to solve any programmatical error - it's to make the syntax easier / nicer.
Compared to having the mutexes explicit you'd save a single line - the mutex declaration. Seriously, is that worth the bother? I see one risk with it: If one happens to use the wrong variable one will silently get another mutex attached to that one. An error would be much better in that case.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", /.../
Which was one of the suggested additional options I had:
synchronized mapping foo = ([]) ...
You didn't explain that. That'd be nice but fairly difficult to implement. (Note that the Synchronized wrapper class you wrote about in 9696453 doesn't achieve it.)
Then isn't it beautiful that you don't HAVE to use that syntax?
Of course not, but I have my doubts about introducing a syntax that has so obvious limitations. That's why I brought it up. It ought to be possible to do better.
Or why not use it with your explicit mutex variable:
synchronized(my_mutex) { modify var 1 and 2 }
You mean synchronized(a_mutex) would lock a_mutex directly while synchronized(not_a_mutex) would automatically associate a mutex with not_a_mutex and then lock it?
You simply don't like the concept of not seeing the mutex lock and the explicit use of it. /.../
I've never said that. I have nothing against a synchronized keyword and that the _locks_ are hidden by synchronized blocks, but I do have my doubts about having the _mutexes_ hidden.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 05:58: Subject: synchronized
I asked how it makes the code more clear, and you responded with that it makes it amazingly clear. Not much of a motivation. I gave at least one concrete case where it isn't: Since there's no mutex name it gets harder to do a grep or similar to find all regions that are synchronized with each other.
Why s this: object lck = my_mutex->lock(); ... destruct(lck)
so much easier to grasp than:
syncronized(mappingfoo) { }
Granted, in the case where synchronized is used with a variable the link is fairly explicit although the mutex itself still is hidden.
Exactly and that is the point - hide the unnecessarily visible code to do the locking. Also when you synchronize a method, it is, as I said in the message you commented, extremely obvious how it works - every call to the method uses the same implicit mutex lock.
I still don't understand what is so unclear / hard about it.
Yes, but I don't think it helps any that the scope of synchronization is implicit. It becomes easier to just use it with no clear understanding about what other methods it also gets synchronized with.
A synchronized methods doesn't get get synchronized with another method at all. I don't understand what you're talking about here.
And in my experience it's absolutely essential to have exactly the right scope for each mutex; using the same one for too many functions is just as disastrous as using it with too few.
Which is exactly why the java versions work so well - each synchronized method has their own mutex (obviously). The same goes for each synchronize-statement that is based on a variable.
Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
Yes, as long as that else is consciously synchronizing on it; it's still just as easy to forget the synchronize block for the variable somewhere and thus get races on it. I think _that_ is a problem worth solving, not just to get a mutex implicitly declared next to the variable.
Of course. It's not meant to solve any programmatical error - it's to make the syntax easier / nicer. I.e saving lines of code an global variables, not improving the code or making it easier not to make mistakes.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", and the compiler would then do all necessary flow analysis and give me errors whenever I broke it. That would be a very useful thing.
Which was one of the suggested additional options I had:
synchronized mapping foo = ([]) ...
Also note "variables"; in almost every case I've had to protect more than one variable with the same mutex. To use the Java synchronized construct for that I'd have to encapsulate them in a small class of their own, which makes it a bit clumsy and incurs an extra unnecessary indirection.
Then isn't it beautiful that you don't HAVE to use that syntax? Or why not use it with your explicit mutex variable:
synchronized(my_mutex) { modify var 1 and 2 }
If you read my example carefully you'll see I didn't talk about that but rather "synchronized (mutex) {...}".
Well, it's exactly the same thing, code-wise (except that if you already have a mutex variable one obviously doesn't need to create and store on in a mapping).
And I see it as a clear feature to have the mutexes explicit. They are important things. They must get absolutely right for everything to work well. They have global implications that can cause unrelated parts of the program to interact in unexpected ways (which is the common cause for deadlocks). I.e. they really require good attention. Making them implicit seems to me like trying to sweep the problem under the mat.
I disagree strongly. The synchronized keyword doesn't make anything less "safe", it simply makes it easier when you need some simple mutex locking.
You simply don't like the concept of not seeing the mutex lock and the explicit use of it. That is an ok opinion and you can continue with that belief. Personally I see this feature as a coding simplification, in many ways similar to implicit lambdas.
/ David Hedbor
The synchronized keyword without arguments could possibly block all other threads ("disallow threads"-like). I've had a few programs where I would have been satisfied with that, even if it's a daft solution. :)
Otherwize, a synchronized like
mapping locks_n_stuff=set_weak_flag(([]),1);
mixed synchronized(mixed var, function f) { object lock;
if (mutexp(var)) lock=var; else if (locks_n_stuff[var]) lock=locks_n_stuff[var]->lock(); else lock=locks_n_stuff[var]=Thread.Mutex();
object key=lock->lock(); // lock(1)? mixed r = f(); destruct(key); return r; }
might work, it could take functions as arguments,
void my_func() { syncronized(my_func) { .... } }
/ Mirar
Previous text:
2003-02-05 04:05: Subject: synchronized
I asked how it makes the code more clear, and you responded with that it makes it amazingly clear. Not much of a motivation. I gave at least one concrete case where it isn't: Since there's no mutex name it gets harder to do a grep or similar to find all regions that are synchronized with each other.
Granted, in the case where synchronized is used with a variable the link is fairly explicit although the mutex itself still is hidden.
The synchronized method format makes it so that the format is never accessed by more than one thread at any time.
Yes, but I don't think it helps any that the scope of synchronization is implicit. It becomes easier to just use it with no clear understanding about what other methods it also gets synchronized with. And in my experience it's absolutely essential to have exactly the right scope for each mutex; using the same one for too many functions is just as disastrous as using it with too few.
Synchronizing on a variable makes it the implicit "lock" (i.e anything else synchronizing on that variable will get a lock).
Yes, as long as that else is consciously synchronizing on it; it's still just as easy to forget the synchronize block for the variable somewhere and thus get races on it. I think _that_ is a problem worth solving, not just to get a mutex implicitly declared next to the variable.
So it'd be another matter if it was a construct where I could declare "these variables may only be accessed synchronized", and the compiler would then do all necessary flow analysis and give me errors whenever I broke it. That would be a very useful thing.
Also note "variables"; in almost every case I've had to protect more than one variable with the same mutex. To use the Java synchronized construct for that I'd have to encapsulate them in a small class of their own, which makes it a bit clumsy and incurs an extra unnecessary indirection.
If you read more in the thread you'll see a construct which exactly handles the 'syncronized(variable) { ... }' case.
If you read my example carefully you'll see I didn't talk about that but rather "synchronized (mutex) {...}".
The whole idea is to avoid having to explicedly define mutex variables, which you often need to keep in global variables. If you always had to specify a mutex, that would remove half the purpose.
And I see it as a clear feature to have the mutexes explicit. They are important things. They must get absolutely right for everything to work well. They have global implications that can cause unrelated parts of the program to interact in unexpected ways (which is the common cause for deadlocks). I.e. they really require good attention. Making them implicit seems to me like trying to sweep the problem under the mat.
/ Martin Stjernholm, Roxen IS
That one seems like a winner to me to add. I mean, it's easy and doesn't harm anything - might as well add it as a feature, perhaps as simple as Thread.synchronize(...) { } where you could import it to remove the Thread part.
/ David Hedbor
Previous text:
2003-02-05 08:39: Subject: synchronized
The synchronized keyword without arguments could possibly block all other threads ("disallow threads"-like). I've had a few programs where I would have been satisfied with that, even if it's a daft solution. :)
Otherwize, a synchronized like
mapping locks_n_stuff=set_weak_flag(([]),1);
mixed synchronized(mixed var, function f) { object lock;
if (mutexp(var)) lock=var; else if (locks_n_stuff[var]) lock=locks_n_stuff[var]->lock(); else lock=locks_n_stuff[var]=Thread.Mutex();
object key=lock->lock(); // lock(1)? mixed r = f(); destruct(key); return r; }
might work, it could take functions as arguments,
void my_func() { syncronized(my_func) { .... } }
/ Mirar
It would only harm if we want synchronized() to have some other semantics in the future. Do we?
But this would work as implicit-lock creator too, so I think it's nice and does what you want<tm>:
syncronized("superlock") { ... } // global keyword :)
class Foo { syncronized(Foo) { ... } // class-global lock syncronized(this_object()) { ... } // object-global lock
mapping m; ... syncronized(m) { ... } // only blocks on the same m instance ... m=copy_value(m); // ignore all current threads working :) }
It's just sad you can't use it like this (or can you?):
class Bar { syncronized my_func() { ... } or syncronized(this_object()) my_func() { ... } }
/ Mirar
Previous text:
2003-02-05 08:45: Subject: synchronized
That one seems like a winner to me to add. I mean, it's easy and doesn't harm anything - might as well add it as a feature, perhaps as simple as Thread.synchronize(...) { } where you could import it to remove the Thread part.
/ David Hedbor
Well, I think it would be unfortunate if it blocks using "synchronized" as a keyword or metaprogram in the future. I'd like something like this:
class X { synchronized (store_mutex) { mapping store = ([]); int last_id = 0; }
int add (mixed data) { synchronized (store_mutex) { store[++last_id] = data; return last_id; } }
mixed get (int id) { // This generates a compile time or runtime error: store is // accessed without holding store_mutex. return m_delete (store, id); } }
But it's not necessarily true that a Thread.synchronized has to interfere with this.
But this would work as implicit-lock creator too, so I think it's nice and does what you want<tm>:
Do you mean an implicit mutex creator? If so, do you actually think it's such a gain to not have to write the mutex declaration that it's worth the risk of silently getting more mutexes than you really want? To me it seems like a variant of the BASIC style implicit variable declarations.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 09:03: Subject: synchronized
It would only harm if we want synchronized() to have some other semantics in the future. Do we?
But this would work as implicit-lock creator too, so I think it's nice and does what you want<tm>:
syncronized("superlock") { ... } // global keyword :)
class Foo { syncronized(Foo) { ... } // class-global lock syncronized(this_object()) { ... } // object-global lock
mapping m; ... syncronized(m) { ... } // only blocks on the same m instance ... m=copy_value(m); // ignore all current threads working :)
}
It's just sad you can't use it like this (or can you?):
class Bar { syncronized my_func() { ... } or syncronized(this_object()) my_func() { ... } }
/ Mirar
I don't see any problems. You have to import Thread to get synchronized in your scope. Thread will not compile if synchronized is made a keyword, but that is easy to fix with #pike.
/ Martin Nilsson (Åskblod)
Previous text:
2003-02-05 15:38: Subject: synchronized
Well, I think it would be unfortunate if it blocks using "synchronized" as a keyword or metaprogram in the future. I'd like something like this:
class X { synchronized (store_mutex) { mapping store = ([]); int last_id = 0; }
int add (mixed data) {
synchronized (store_mutex) { store[++last_id] = data; return last_id; } }
mixed get (int id) {
// This generates a compile time or runtime error: store is // accessed without holding store_mutex. return m_delete (store, id); } }
But it's not necessarily true that a Thread.synchronized has to interfere with this.
But this would work as implicit-lock creator too, so I think it's nice and does what you want<tm>:
Do you mean an implicit mutex creator? If so, do you actually think it's such a gain to not have to write the mutex declaration that it's worth the risk of silently getting more mutexes than you really want? To me it seems like a variant of the BASIC style implicit variable declarations.
/ Martin Stjernholm, Roxen IS
Well, I think it would be unfortunate if it blocks using "synchronized" as a keyword or metaprogram in the future. I'd like something like this:
class X { synchronized (store_mutex) {
Yes, me too. It could possible be a global definition for now, working inside functions.
Do you mean an implicit mutex creator?
I don't know if implicit is the right word to use. But I suppose so...
If so, do you actually think it's such a gain to not have to write the mutex declaration that it's worth the risk of silently getting more mutexes than you really want?
Yes. I often want to lock a single variable, or a single object. It seems logical to lock that variable, or the object by giving that to synchronized(). I don't see that step very far from using the synchronized keyword, and locking the mutex without calling lock().
/ Mirar
Previous text:
2003-02-05 15:38: Subject: synchronized
Well, I think it would be unfortunate if it blocks using "synchronized" as a keyword or metaprogram in the future. I'd like something like this:
class X { synchronized (store_mutex) { mapping store = ([]); int last_id = 0; }
int add (mixed data) {
synchronized (store_mutex) { store[++last_id] = data; return last_id; } }
mixed get (int id) {
// This generates a compile time or runtime error: store is // accessed without holding store_mutex. return m_delete (store, id); } }
But it's not necessarily true that a Thread.synchronized has to interfere with this.
But this would work as implicit-lock creator too, so I think it's nice and does what you want<tm>:
Do you mean an implicit mutex creator? If so, do you actually think it's such a gain to not have to write the mutex declaration that it's worth the risk of silently getting more mutexes than you really want? To me it seems like a variant of the BASIC style implicit variable declarations.
/ Martin Stjernholm, Roxen IS
Even in that case the declaration for the variable ought have some sort of flag that it has a mutex, i.e. that the mutex still is declared in one way or the other. E.g:
synchronized mapping foo; mapping bar; ... void f() { synchronized (foo) {...} // Ok. synchronized (bar) {...} // Error: bar doesn't have a mutex. }
There are two reasons for this requirement: As I've said earlier it avoids silently getting mutexes on the wrong things when one happens to mixup variables. Another reason is that it allows more efficient implementation since the space for a mutex can be allocated at compile time. (Note that the proposed solution with a weak mapping is prone to cause gc overhead since the locked things can't be refcount garbed.)
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 16:36: Subject: synchronized
Well, I think it would be unfortunate if it blocks using "synchronized" as a keyword or metaprogram in the future. I'd like something like this:
class X { synchronized (store_mutex) {
Yes, me too. It could possible be a global definition for now, working inside functions.
Do you mean an implicit mutex creator?
I don't know if implicit is the right word to use. But I suppose so...
If so, do you actually think it's such a gain to not have to write the mutex declaration that it's worth the risk of silently getting more mutexes than you really want?
Yes. I often want to lock a single variable, or a single object. It seems logical to lock that variable, or the object by giving that to synchronized(). I don't see that step very far from using the synchronized keyword, and locking the mutex without calling lock().
/ Mirar
That looks nicer then my solution. If you can get that to work, I'm all for it. It also has the benefit that it will syncrhonize on the variable, not the value.
/ Mirar
Previous text:
2003-02-05 18:29: Subject: synchronized
Even in that case the declaration for the variable ought have some sort of flag that it has a mutex, i.e. that the mutex still is declared in one way or the other. E.g:
synchronized mapping foo; mapping bar; ... void f() { synchronized (foo) {...} // Ok. synchronized (bar) {...} // Error: bar doesn't have a mutex. }
There are two reasons for this requirement: As I've said earlier it avoids silently getting mutexes on the wrong things when one happens to mixup variables. Another reason is that it allows more efficient implementation since the space for a mutex can be allocated at compile time. (Note that the proposed solution with a weak mapping is prone to cause gc overhead since the locked things can't be refcount garbed.)
/ Martin Stjernholm, Roxen IS
It appears to be difficult unless one accept some limitations.
If the expression in synchronized() evaluates to a normal svalue it's too late to go back to the variable it came from. To avoid that it'd be necessary to evaluate to an lvalue, but that won't work in cases like synchronized(my_object->foo) where my_object has a custom `->.
I don't think that's a major shortcoming, though; in such odd cases one can just give the mutex a name of its own.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 18:33: Subject: synchronized
That looks nicer then my solution. If you can get that to work, I'm all for it. It also has the benefit that it will syncrhonize on the variable, not the value.
/ Mirar
Looks resonable.
/ Peter Bortas
Previous text:
2003-02-05 18:29: Subject: synchronized
Even in that case the declaration for the variable ought have some sort of flag that it has a mutex, i.e. that the mutex still is declared in one way or the other. E.g:
synchronized mapping foo; mapping bar; ... void f() { synchronized (foo) {...} // Ok. synchronized (bar) {...} // Error: bar doesn't have a mutex. }
There are two reasons for this requirement: As I've said earlier it avoids silently getting mutexes on the wrong things when one happens to mixup variables. Another reason is that it allows more efficient implementation since the space for a mutex can be allocated at compile time. (Note that the proposed solution with a weak mapping is prone to cause gc overhead since the locked things can't be refcount garbed.)
/ Martin Stjernholm, Roxen IS
I don't have a major problem with that. And yes, the weak mapping solution is a hack / temporary solution rather than what I'd really like to get implemented.
In your example would the synchronized(bar) case be a compile time or run time error? Actually, it can't be compile time since the variable you want to lock might be run-time specified. As a matter of fact, doesn't the requirement of 'synchronized' declaration limit the usage area quite a lot?
I mean, say you have a method that is called from any thread:
void process_something(mixed var) { synchronized(var) { process ... } }
Now everything calling this method would have to use 'synchronized' variables. Since it's impossible to know WHAT would be calling this method at compile time, it has to be a runtime error. I don't like that at all. It should just work since there is no inherit need to do anything before "locking" the variable. Sure, you can pre-allocate a mutex but how would that help? Would each svalue contain a pointer to a mutex? That makes no sense since a pointer typically would use the same amount of memory as a mutex lock - how would you indicate that an svalue has a mutex in a way that makes the "preallocate mutex" make any sense?
/ David Hedbor
Previous text:
2003-02-05 18:29: Subject: synchronized
Even in that case the declaration for the variable ought have some sort of flag that it has a mutex, i.e. that the mutex still is declared in one way or the other. E.g:
synchronized mapping foo; mapping bar; ... void f() { synchronized (foo) {...} // Ok. synchronized (bar) {...} // Error: bar doesn't have a mutex. }
There are two reasons for this requirement: As I've said earlier it avoids silently getting mutexes on the wrong things when one happens to mixup variables. Another reason is that it allows more efficient implementation since the space for a mutex can be allocated at compile time. (Note that the proposed solution with a weak mapping is prone to cause gc overhead since the locked things can't be refcount garbed.)
/ Martin Stjernholm, Roxen IS
I don't have a major problem with that. And yes, the weak mapping solution is a hack / temporary solution rather than what I'd really like to get implemented.
Can't think of any other way to associate mutexes with values without incurring some overhead on values where they aren't used.
Now everything calling this method would have to use 'synchronized' variables. Since it's impossible to know WHAT would be calling this method at compile time, it has to be a runtime error. I don't like that at all.
Why not? I don't think it's that different from the runtime type checks everywhere. What's the use of silently promoting unsynchronized values to synchronized ones? When you create the value you ought to know if you plan to use it synchronized or not. If a value starts out as unsynchronized the risk is obvious that it also is used without proper locking by the code that created it.
It should just work since there is no inherit need to do anything before "locking" the variable.
There's no inherent need to declare a variable before using it either, but most languages demand it anyway since it provides means to check for various errors.
Would each svalue contain a pointer to a mutex?
No, that's one place where it definitely can't be. I'd like them to be allocated next to the variables that need locking, even though that has its limitations, e.g. the case you brought up where a plain synchronized value is passed around would need it to be wrapped:
class SynchVal { synchronized mixed val; }
void process_something (SynchVal x) { synchronized (x->val) {...} }
Although a bit clumsier I don't think that's a bad thing since it provides good compile time and runtime checking.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 20:35: Subject: synchronized
I don't have a major problem with that. And yes, the weak mapping solution is a hack / temporary solution rather than what I'd really like to get implemented.
In your example would the synchronized(bar) case be a compile time or run time error? Actually, it can't be compile time since the variable you want to lock might be run-time specified. As a matter of fact, doesn't the requirement of 'synchronized' declaration limit the usage area quite a lot?
I mean, say you have a method that is called from any thread:
void process_something(mixed var) { synchronized(var) { process ... } }
Now everything calling this method would have to use 'synchronized' variables. Since it's impossible to know WHAT would be calling this method at compile time, it has to be a runtime error. I don't like that at all. It should just work since there is no inherit need to do anything before "locking" the variable. Sure, you can pre-allocate a mutex but how would that help? Would each svalue contain a pointer to a mutex? That makes no sense since a pointer typically would use the same amount of memory as a mutex lock - how would you indicate that an svalue has a mutex in a way that makes the "preallocate mutex" make any sense?
/ David Hedbor
No, that's one place where it definitely can't be. I'd like them to be allocated next to the variables that need locking, even though that
Huh? Allocated "next to the variables"? What do you mean with that?
/ David Hedbor
Previous text:
2003-02-06 01:03: Subject: synchronized
I don't have a major problem with that. And yes, the weak mapping solution is a hack / temporary solution rather than what I'd really like to get implemented.
Can't think of any other way to associate mutexes with values without incurring some overhead on values where they aren't used.
Now everything calling this method would have to use 'synchronized' variables. Since it's impossible to know WHAT would be calling this method at compile time, it has to be a runtime error. I don't like that at all.
Why not? I don't think it's that different from the runtime type checks everywhere. What's the use of silently promoting unsynchronized values to synchronized ones? When you create the value you ought to know if you plan to use it synchronized or not. If a value starts out as unsynchronized the risk is obvious that it also is used without proper locking by the code that created it.
It should just work since there is no inherit need to do anything before "locking" the variable.
There's no inherent need to declare a variable before using it either, but most languages demand it anyway since it provides means to check for various errors.
Would each svalue contain a pointer to a mutex?
No, that's one place where it definitely can't be. I'd like them to be allocated next to the variables that need locking, even though that has its limitations, e.g. the case you brought up where a plain synchronized value is passed around would need it to be wrapped:
class SynchVal { synchronized mixed val; }
void process_something (SynchVal x) { synchronized (x->val) {...} }
Although a bit clumsier I don't think that's a bad thing since it provides good compile time and runtime checking.
/ Martin Stjernholm, Roxen IS
That the mutex is allocated in the same object that contains the variable(s) it protects. E.g.
synchronized mapping foo;
would allocation-wise be similar to
Thread.Mutex foo_mutex; mapping foo;
Note that it's the variable foo itself that's the principal point of the mutex protection, not its value. One advantage with that is that it's possible to protect simple types like integers and strings where it would be meaningless to associate a mutex with the value.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-06 03:51: Subject: synchronized
No, that's one place where it definitely can't be. I'd like them to be allocated next to the variables that need locking, even though that
Huh? Allocated "next to the variables"? What do you mean with that?
/ David Hedbor
I like that. One could also give them an identifier so that they can be locked outside the object, and used for objects overloading `[]. (Say, "`mutex_lock <variable>".)
class A { syncronized mixed foo; }
A a=A(); syncronized(a->foo) { ... }
But now it gets tricky to implement. :) But not that tricky, since syncronized() already must take an lvalue.
/ Mirar
Previous text:
2003-02-06 04:22: Subject: synchronized
That the mutex is allocated in the same object that contains the variable(s) it protects. E.g.
synchronized mapping foo;
would allocation-wise be similar to
Thread.Mutex foo_mutex; mapping foo;
Note that it's the variable foo itself that's the principal point of the mutex protection, not its value. One advantage with that is that it's possible to protect simple types like integers and strings where it would be meaningless to associate a mutex with the value.
/ Martin Stjernholm, Roxen IS
If the mutex should be accessible by itself then just give it a name:
synchronized(foo_mutex) mixed foo;
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-06 10:20: Subject: synchronized
I like that. One could also give them an identifier so that they can be locked outside the object, and used for objects overloading `[]. (Say, "`mutex_lock <variable>".)
class A { syncronized mixed foo; }
A a=A(); syncronized(a->foo) { ... }
But now it gets tricky to implement. :) But not that tricky, since syncronized() already must take an lvalue.
/ Mirar
1) Anyone against adding that to Threads.pmod?
2) Is the above code thread-safe without adding extra locks?
/ Mirar
Previous text:
2003-02-05 08:39: Subject: synchronized
The synchronized keyword without arguments could possibly block all other threads ("disallow threads"-like). I've had a few programs where I would have been satisfied with that, even if it's a daft solution. :)
Otherwize, a synchronized like
mapping locks_n_stuff=set_weak_flag(([]),1);
mixed synchronized(mixed var, function f) { object lock;
if (mutexp(var)) lock=var; else if (locks_n_stuff[var]) lock=locks_n_stuff[var]->lock(); else lock=locks_n_stuff[var]=Thread.Mutex();
object key=lock->lock(); // lock(1)? mixed r = f(); destruct(key); return r; }
might work, it could take functions as arguments,
void my_func() { syncronized(my_func) { .... } }
/ Mirar
else if (locks_n_stuff[var]) lock=locks_n_stuff[var]->lock(); else lock=locks_n_stuff[var]=Thread.Mutex();
That could create a race condition couldn't it? When does thread switching potentially occur? Between each opcode?
/ David Hedbor
Previous text:
2003-02-05 15:13: Subject: synchronized
Anyone against adding that to Threads.pmod?
Is the above code thread-safe without adding extra locks?
/ Mirar
Perhaps now would be a good time to define exactly how successor-to-implicit-lambda would look like and work?
/ Martin Nilsson (Åskblod)
Previous text:
2003-02-05 01:00: Subject: synchronized
I've never understood benefit of hiding mutexes like that Java construct does. It even does so in three different ways depending on how it's used. Is the code somehow made more clear because the mutexes disappear? I prefer to see them, so I can tell easily which regions are synchronized with each other. That also makes it easier to search on the mutex variable to find those regions.
There's one benefit with a block construct like that: It makes the region where the lock is held more clear. (The pike way of relying on refcount garbing of the lock has lead to many bugs, e.g. with the tail recursion optimization. I don't rely on refcounting for locks anymore; I always zero them explicitly afterwards.)
So in my view a good synchronized construct would always require a mutex argument. E.g:
Thread.Mutex my_mutex = Thread.Mutex(); synchronized(my_mutex) void my_first_function() {...}
void my_second_function() { ... synchronized(my_mutex) { ... } }
The second case above is solvable with implicit lambda, but I'd rather avoid implementing it that way since it "destroys" the syntax so it can't be upgraded to a better implementation later on (see the blurb about implicit lambdas in the CHANGES file).
As a transitionary measure, we could perhaps have something like this instead that works through implicit lambdas:
my_mutex->synchronize() { ... };
/ Martin Stjernholm, Roxen IS
Doesn't that require a fairly good idea how the metaprogram interface will look like? At least I don't have that.
/ Martin Stjernholm, Roxen IS
Previous text:
2003-02-05 15:29: Subject: synchronized
Perhaps now would be a good time to define exactly how successor-to-implicit-lambda would look like and work?
/ Martin Nilsson (Åskblod)
If we want to be safe, yes you are unfortunately right.
/ Martin Nilsson (Åskblod)
Previous text:
2003-02-05 15:48: Subject: synchronized
Doesn't that require a fairly good idea how the metaprogram interface will look like? At least I don't have that.
/ Martin Stjernholm, Roxen IS
pike-devel@lists.lysator.liu.se