|
Boost : |
From: Douglas Gregor (gregod_at_[hidden])
Date: 2000-11-27 18:29:49
On Mon, 27 Nov 2000 22:01:37 -0000
"William Kempf" <sirwillard_at_[hidden]> wrote:
> --- In boost_at_[hidden], Douglas Gregor <gregod_at_r...> wrote:
> > On Thu, 23 Nov 2000 14:45:57 -0000
> > "William Kempf" <sirwillard_at_m...> wrote:
> >
> > [snip]
> > > *shrug*
> > >
> > > I'm *slightly* leaning towards cloning instead of ref-counting at
> > > this point. In general it's safer, and as pointed out, when this
> >
> > In general cloning is safer, but statics, pointers, references, and
> data members are all unsafe even with cloning. My general fear is
> that we are guaranteeing too much by calling cloning safe in
> multithreaded environments (and then listing all of the exceptions).
> Instead, I would suggest that we use reference-counting and
> explicitly state that the function object must itself be thread-safe
> to guarantee thread safety. I also expect that any Boost threads
> library would contain a function object adaptor to mutex any function
> object. This makes it the user's responsibility to make a function
> object thread-safe, but at least it is stated that it is never safe
> to assume thread-safety.
>
> Obviously the function object itself must be thread safe, regardless
> of copying semantics. I wasn't discussing such things, however, only
> the act of copying. Copying using ref-counting is problematic, while
> cloning is generally safer (provided the "clone" routine is thread
> safe... which may be an argument yet again in favor of ref-counting,
> where the thread safety can be externalized).
>
> > Also, I've been trying to consider how often cloning would be more
> thread-safe than ref-counting, and as far as I can see these are the
> most common uses of function objects:
> > 1) Wrapping free and member functions (e.g., ptr_fun)
> > 2) Performing some operation on the arguments and returning
> the result (e.g., negate, plus)
> > 3) Binding arguments (e.g., bind1st, bind2nd, lambda)
>
> 4) Maintaining state across calls, but specific to the function
> object's instance (something which can not be done with true
> functions).
>
> > #1 and #2 won't be more safe using cloning. #3 is safe because the
> bound arguments are immutable anyway. Other function object types may
> be safer with cloning, but I doubt these occur with great frequency.
>
> (4) is very common, and is in general often safer using cloning.
> However, it also changes some semantics and is not usable in a lot of
> cases (where the function object is passed by value, thus cloning
> with every pass and therefor messing with the state).
>
> I'm still on the fence. I see problems with both sides, but not
> clear cut instances where one would be preferred over the other.
In a GUI system it is very common to have several callbacks target the same function object (e.g., a menu item, a keyboard accelerator, and a toolbar button all performing the same function). I may concisely write this as:
myButton.onClick = myMenuItem.onSelect = myKeyboardAccel.onPressed = make_callback(some_functor);
If I were to diagram where these events are going, I would of course have them pointing to an instance "some_functor". If I were concerned with multiple threads, it would be an obvious issue and I would write some_functor with the appropriate synchronization barrier. Since each event "means" the same thing, creating separate instances is unnatural (or wrong, depending on some_functor).
> > > implementation becomes detrimental you can wrap it in a ref-
> counting
> > > proxy (whether it be shared_ptr or some other proxy). Going the
> > > other way, for example if it's deemed too dangerous for threading
> > > use, is not really possible.
> >
> > This is case where the desire to support multithreaded code is
> detrimental to single-threaded code.
>
> I don't think the two issues parallel. This particular issue has
> several work arounds that won't work as nicely for the choice between
> ref-counting or cloning.
>
> > Either the degradation is in performance, or the degradation is in
> the remedy to the performance problem - using shared_ptrs makes for
> ugly usage of the callback code.
>
> Not necessarily. Especially if the "proxy" supports operator().
Using shared_ptr (with the operator() overloads) will still require a "new" when assigning to callbacks, e.g.:
shared_ptr< callback<int, int> > cb = new make_callback(some_function);
Of course, a derivative of shared_ptr could be used, so this isn't a major issue.
Doug Gregor
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk