EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  <20052006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  <20052006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: FW: Standard String
From: Benjamin Franksen <[email protected]>
To: [email protected]
Date: Tue, 19 Jul 2005 23:49:08 +0200
On Tuesday 19 July 2005 17:00, Jeff Hill wrote:
> > From: Andrew Johnson [mailto:[email protected]]
> > The main thing that bothers me is your StreamPosition interface. 
> > The position() and movePosition() methods implies that every
> > StringSegment includes an iterator into the string, which I assume
> > is used by the StreamRead and StreamWrite methods to indicate where
> > their next access will occur.
> > It's only possible to have one such iterator per
> > StringSegment, so it's not possible for two threads to access the
> > same string simultaneously, even if they're both only reading from
> > it.
>
> Therefore two conclusions:
>
> 1) Two threads must not have read or write access to the same string
> simultaneously, and therefore some sort of mutex locking is required
> - nothing new here.

AFAICS, there is no need to pack the streamRead interface into the class 
that represents the string itself. Instead, a streamRead object should 
be created from an existing string whenever read access to the string 
is needed. Thus, each thread uses its very own (stack allocated) 
streamRead object. A streamRead object is more or less a read iterator 
over a string. It will contain some additional methods from the 
streamPosition interface (like position and movePosition). It should 
not be passed around (and there is no need to).

What for write locks? Not necessary, either. We simply don't give write 
access to (already constructed) strings. Think about it: do you know 
any example where you actually need to destructively update a string, 
once you have it constructed?

There may be code examples where such update is done as a low-level 
optimization. That is, you overwrite an existing string instead of 
first throwing it away, and then creating a new one. Apart from being 
dangerous, I doubt very much that this kind of optimization is really a 
big win, if you take the additional amount of necessary locking and 
copying into account.

[One could even experiment with a reference counted string 
implementation and find out how the avoidance of copying when passing 
strings weighs against the necessary locking of the reference count 
updates.]

Most of the time (at least in the IOC, but I believe this is true for 
most clients, too) strings are merely copied from one place to another, 
or else newly constructed from existing pieces.

For creating a new string, we need some variant of streamWrite object, 
but again, this should be a separate object. If construction is done 
(possibly after many "write" operations), a new string is constructed 
from the streamWrite object, which is then discarded. And again, I 
doubt that write access is needed at arbitrary positions. Having the 
write position fixed at the end of the string under construction is 
general enough for any application I can think of. With such an 
invariant, optimizations under the hood can be used to minimize the 
amount of copying when creating the new string from the streamWrite 
object.

> >From my perspective, one must either include the lock in the class,
> > use
>
> guard classes to guarantee that locking contracts are executed, or
> let codes that use the string class remember what locking is required
> (no compiler enforcement).

Or use non-modifiable strings and forget about locking.

Respectively, for a reference counting scheme, locking is done only when 
passing a string or when a string gets destroyed, and then only for an 
extremely short period (one integer increment), so it can be drastic 
and cheap (e.g. intLock in vxWorks).

> Of course, the StreamPosition / StreamRead / StreamWrite interfaces
> could be passed separately from the rest of the string interface, but
> this would just about eliminate use of strings with templates as
> templates require one entity (template argument) implementing any
> type that gets used. Therefore, some temporary helper objects
> implementing the StreamPosition / StreamRead / StreamWrite interfaces
> might be instantiated on the stack for the duration of the call to
> the CA/DB function reading/writing a string.

Yes, this is the way to go, with the only difference that

o strings can't be modified
o a string can be created from a (temporary) streamWrite object
o a (temporary) streamRead object can be created from a string

[With "temporary" I mean stack allocated, here, not to be confused with 
the C++ concept of "temporary object".]

> Another alternative would be to have a function in the core string
> interface that returns a reference to the StreamPosition / StreamRead
> / StreamWrite interfaces, but I decided against adding a method that
> returns an indirect handle to internal storage. Lock management is
> complex with that approach (or at least requires a guard class in the
> interface), and with that approach there is a stronger possibility of
> some user using a StreamPosition / StreamRead / StreamWrite against a
> target object that no longer exists.

Yes.

> Also, if you eliminate StreamPosition / StreamRead / StreamWrite and
> the methods in StringSegment that manipulate the current position
> there isn't much left.

And this is how it should be. The string itself has almost no 
properties, but it can be freely passed around as a value.

> > If the V3 IOC used StringSegment instead of char* for storing
> > record names, that would means that as long as a lockset was
> > locked, the CA UDP task would not be able to complete a search
> > where the record name it's looking for is lower down the same hash
> > bucket as a record in that lockset (since the only record-related
> > mutex we have in V3 is in the lockset, so you'd have to take that
> > before you'd be allowed to change the current position of the name
> > string).
> >
> > I don't think that's acceptable, and I doubt that you would either.
> >  A string is not an iterator and should not contain one, since it
> > prevents simultaneous access to two different parts of the same
> > string, even from inside the same function.
>
> As mentioned above, the StreamPosition / StreamRead / StreamWrite
> interfaces might need to be temporarily inflated thread private
> state, but nevertheless passed along through the CA and DB interfaces
> that manipulate strings. Therefore, the lock set wouldn't need to be
> locked.

I get very cold feet whenever I hear the word "thread private state". 
Don't do that, there are better ways.

> As soon as you have on line addition of records and or changes to
> record names then you certainly will need to lock out the CA UDP
> thread at certain times.

Online add & delete of records will be quite rare, compared to name 
lookup.

Ben

Replies:
Re: Standard String Kay-Uwe Kasemir
References:
FW: Standard String Jeff Hill

Navigate by Date:
Prev: Re: Standard String Kay-Uwe Kasemir
Next: Re: Standard String Kay-Uwe Kasemir
Index: 2002  2003  2004  <20052006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: FW: Standard String Jeff Hill
Next: Re: Standard String Kay-Uwe Kasemir
Index: 2002  2003  2004  <20052006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 02 Feb 2012 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·