Robert Dober
12/26/2008 6:17:00 PM
On Fri, Dec 26, 2008 at 11:24 AM, Robert Klemme
<shortcutter@googlemail.com> wrote:
> On 26.12.2008 10:43, Robert Dober wrote:
>>
>> I would very much welcome a set of guaranteed-thread-safe wrapper
>>>
>>> collections folks could use if they're concerned about concurrency, sin=
ce
>>> it
>>> would be unreasonable to penalize all code with locking. For now, learn
>>> and
>>> love Mutex, or don't share collections across threads.
>>
>> That sounds like a fun job. The issue goes well with a question which
>> burns on the top of my tongue:
>> How to test thread issues? I do not have any experience in this field
>> but consider it somehow critical before "promising" functional thread
>> safe wrappers.
>
> This is tricky. There are few things I'd like to say to this: first, the=
re
> is an easy way to provide _basic_ thread safety by wrapping all method ca=
lls
> in a synchronized block - for this even a SynchronizedDelegator would be
> sufficient which could be used for _all_ classes - not just collections. =
In
> this case, because of the way the wrapping is done there is no need to te=
st
> thread safety because - as long as the delegator ensures that all methods
> are wrapped in this way there is no chance of corrupting internal state.
>
> But, in the general case thread safety cannot be achieved on the class
> level. My typical example is this
>
> if hash.contains_key? k
> dat =3D hash[k]
> else
> dat =3D create_dat(k)
> hash[k] =3D dat
> end
>
> The basic property of this bit of code which makes it impossible to ensur=
e
> thread safety on level of class Hash is that there are two methods invoke=
d
> on the same instance and there must not be any state change between them
> because then you either end up with garbage (nil) in "dat" or you invoke
> create_dat(k) more than once per key value and this leads to different
> threads having a different idea of what hash[k] is.
>
> So in this case you need a lock around the _complete_ block of code. (Not=
e,
> the method level lock would work if using a Hash with a default block, bu=
t
> this solves only a small portion of the cases.) This is also the reason =
why
> a general per method locking is only of limited use. It only ensures
> consistency of the internal state of an object but can never ensure overa=
ll
> concurrency correctness of an application.
>
> Testing thread safety is difficult to impossible for several reasons. One=
of
> the reasons is that for proper control of the test case you would need to
> ensure exact timing of thread execution. While you could do that I have
> never seen people actually doing this - maybe because this will make test
> suites run much slower. Another reason is that you vastly depend on the
> underlying machine (i.e. hardware, OS and VM). I have seen Java programs
> break as soon as they were executed on a multi core machine with more tha=
n n
> cores.
>
> Things aren't made easier by the fact that - concluding from postings I s=
ee
> on comp.lang.java.* newsgroups for example - few people seem to have a
> thorough understanding of concurrency and all the issues involved. Anothe=
r
> item on the "makes it hard" list is the fact that most OO and procedural
> languages only have a very basic toolkit for concurrency control; while J=
ava
> started out pretty good with built in "synchronized" it took until versio=
n 5
> that they incorporated Doug Lea's concurrency utilities into the language=
's
> standard library and also into the EJB spec.
>
> It's different in other programming languages: functional languages are b=
y
> definition thread safe because they are free of side effects. (At least =
in
> theory. :-)) Also, other languages built for concurrent applications whi=
ch
> have a different programming model (e.g. Occam) of course have much bette=
r
> support for concurrency.
>
> Kind regards
>
> robert
>
>
> --
> remember.guy do |as, often| as.you_can - without end
>
>
Robert, IIUC we do not want either the one, nor the other.
For what I am concerned one would need Read/Write Locks.
The built in methods like [] and []=3D would obtain read and write
locks, while the read lock automatically obtains a write lock and only
releases it when its count is to zero, the write lock would inhibit
the read lock to be obtained.
I do not know if one should allow user access to these locks? My first
thought would be no, all we want to do is to have thread-safe Hash,
Array, String but not to provide advanced synchronization mechanisms.
OTOH one could expose the write_lock and read_lock of each object
which would allow for the following
hash =3D TS_Hash::new
...
dat =3D hash.read_synchronize do
if hash.has_key? key then
hash[ key ]
else
hash.write_synchronize do
hash[key] =3D compute_data( key )
end
end
end
N.B. We have to solve the interlock problem here as normally the
write_synchronize does not obtain the write_lock
as the read_synchronize did get a read_lock. We would need to grant
the write lock if the read_lock is obtained by the current thread
*only*, but imagine two threads waiting for the write_lock while
containing the read_lock, the good old
philosophers-forks-spoon-spaghetti interlock. [ That is why we eat
spaghetti with a fork only ;) ]
Therefore I guess the RW-locking in a ThreadSafe Container class shall
rather not be exposed as we avoiding interlocking is not that
complicated IIRC
And if we expose read_synchronize &blk, and write_synchronize &blk we
should probably raise something like an IllegalMonitorState exception
if a thread tries to call the one inside the other.
More thoughts please.
Cheers
Robert
--=20
Il computer non =E8 una macchina intelligente che aiuta le persone
stupide, anzi, =E8 una macchina stupida che funziona solo nelle mani
delle persone intelligenti.
Computers are not smart to help stupid people, rather they are stupid
and will work only if taken care of by smart people.
Umberto Eco