Hm I hate reasoning about edge-cases... they make life so much harder.
Guess I'll have to think about this some more.
My original question was about how to benchmark my own version (fairly)
against boost, hence this mailing list. As far as I was concerned, this
discussion could have been over after the link to all the benchmarks. I
never intended this discussion to be about thread safety. However, I can't
stress enough that I'm very thankful for all the help! ;-)
The reason I wrote it in the first place was just for fun I guess. After
having used the idiom in Qt, it seemed like a nice challenge. Now that it's
almost finished, I'd like te see how it holds up against the big guys.
Also, it's just a little different than what I've seen of the others, with
what I think is a nice syntax.
Cheers,
Joren
P.S. Is that implementation of your sqrt() available on github? Seems
interesting! ;-)
On Feb 7, 2015 7:07 AM, "Gottlob Frege"
Thanks for the many responses and suggestions. However, I'm not sure if everything applies to my particular application. If it does, I don't
fully understand...
My implementation provides an Emitter class (template), rather than a signal class. Signals are types which act as template parameters to the Emitter. For example:
using Event1 = Signal
; using Event2 = Signal ; Emitter
em; // normally, you'd probably derive from this em.connect<Event1>(someFunction); em.connect<Event2>(otherFunction); em.emit<Event1>(); em.emit<Event2>(42); Each Signal-type has its own corresponding vector of slots defined within the Emitter. Calling connect/disconnect adds/removes a slot to/from this vector and calling emit() results in iterating over it and calling all its slots (this is a read-only operation).
I can see trouble arising when thread 1 is iterating the vector while
On Fri, Feb 6, 2015 at 8:11 PM, Joren Heit
wrote: think I thread 2 is modifying it. Would it be an idea to have the emitting thread 1. lock the vector, 2. make a local copy, 3. unlock, and 4. iterate the copy? This way, the modifying thread only needs to wait for the copy being made instead of every slot being called and executed. Does that make sense at all?
Yeah, you are on the right track. Making a copy of data while holding a lock then processing the copy without the lock is often a good strategy in threading. However, in this case...
int * somePtr = nullptr;
void someFunction() { // can somePtr change after the check but before the set? if (somePtr) *somePtr = 17; }
void cleanupPtr() { // this looks safe, but compilers and CPUs can reorder this code: int * tmp = somePtr; somePtr = null; delete tmp; }
void thread1() { em.emit<Event1>(); }
void thread2() { em.remove<Event1>(someFunction); // now safe to cleanup (?) cleanupPtr(); }
Now lets say the emit and the remove are happening "at the same time", and - Thread1: emit gets to the lock first, makes a copy, and unlocks - Thread2: remove comes in, gets the lock, removes someFunction, returns - Thread1: calls someFunction as part of iterating over copy of list - Thread1: someFunction checks somePtr, sees it is non-null, great! (?) - Thread2: after returning from remove, calls cleanupPtr - Thread1: either writes to deleted memory, or writes to null
Threading is fun!
Thanks again for all the help. Scott Meyers was right in his lectures about this being a helpful community! Oh, and if my formatting is screwed up, I'm truly sorry, but I'm writing this on my phone.
You are actually borderline not (or no longer) talking about boost, but your own code/implementation. Some might call that questionable for a boost list. But threading is fun!
P.S. why write your own - why not use boost? Because of performance? Was that the original point? P.P.S. correctness is typically better than performance. I've written the world's (second) fastest square-root. It returns 17. Not very accurate, but very fast.
Tony _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users