Re: synchronization between two process without lock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2017-09-01 6:10 GMT+08:00 Paul E. McKenney <paulmck@xxxxxxxxxxxxxxxxxx>:
> On Thu, Aug 24, 2017 at 02:30:41AM +0800, Yubin Ruan wrote:
>> Hi paul,
>>
>> 2017-08-08 10:12 GMT+08:00 Yubin Ruan <ablacktshirt@xxxxxxxxx>:
>> > 2017-08-08 0:57 GMT+08:00 Paul E. McKenney <paulmck@xxxxxxxxxxxxxxxxxx>:
>> >> On Tue, Aug 08, 2017 at 12:48:08AM +0800, Yubin Ruan wrote:
>> >>> 2017-08-05 9:02 GMT+08:00 Yubin Ruan <ablacktshirt@xxxxxxxxx>:
>> >>> > 2017-08-05 3:50 GMT+08:00 Paul E. McKenney <paulmck@xxxxxxxxxxxxxxxxxx>:
>> >>> >> On Fri, Aug 04, 2017 at 11:57:28PM +0800, Yubin Ruan wrote:
>> >>> >>> 2017-08-04 22:52 GMT+08:00 Yubin Ruan <ablacktshirt@xxxxxxxxx>:
>> >>> >>> > Hi,
>> >>> >>> > I am sure the subject explain my intention. I got two processes trying
>> >>> >>> > to modifying the same place. I want them to do it one after one, or,
>> >>> >>> > if their operations interleave, I would like to let them know that the
>> >>> >>> > content have been changed and polluted by the other so that the
>> >>> >>> > content should be given up. That is, I would rather give up the data,
>> >>> >>> > if polluted, than having a false one.
>> >>> >>> >
>> >>> >>> > I try to set a atomic ref counter, but it seems impossible to do that
>> >>> >>> > without a lock to synchronize.
>> >>> >>> >
>> >>> >>> > Note that I don't want a strict synchronization: the situation is a
>> >>> >>> > lot better. The data can be given up if that place has been polluted.
>> >>> >>>
>> >>> >>> Let's explain some of my reasoning: if process A use some flag to
>> >>> >>> indicate that it has entered the critical region, then if it crash
>> >>> >>> before it can reset the flag, all following processes cannot enter
>> >>> >>> that region. But if process A cannot use flag for indication, how to
>> >>> >>> other people know (how to synchronization)?
>> >>> >>
>> >>> >> The simplest approach is to guard the data with a lock.
>> >>> >
>> >>> > Indeed. But if a process get killed then it will have no chance to release
>> >>> > the lock...
>> >>> >
>> >>> > By the way, do you know whether there are any chances that a thread get
>> >>> > killed by another thread when doing some "small" things? I mean something
>> >>> > like this:
>> >>> >
>> >>> >     lock();
>> >>> >     some_mem_copying();
>> >>> >     unlock();
>> >>> >
>> >>> > Are there any chance that a thread get killed by another thread before it
>> >>> > can "unlock()", without the entire process going down?
>> >>
>> >> Indeed, that is possible.  The pthread_kill() system call if nothing
>> >> else.
>> >>
>> >>> pthread_mutexattr_setrobust(..) will help in this situation, although it is
>> >>> quite painful that nobody is maintaining the NPTL docs currently and you have
>> >>> to dive into the source code if you want to make sure the semantic is exactly
>> >>> what you want.
>> >>
>> >> True on all counts.  But what exactly are you trying to achieve?
>> >
>> > What I am going to achieve is to allow multiple producer to place some content
>> > in some slots of a queue. The risk is that some producers might be in the same
>> > slot, so that it require some locking to synchronize them. But if you use lock,
>> > a thread holding the lock can get killed accidentally (for whatever reasons)
>> > before releasing it, causing all the other producers unable to acquire it
>> > therefore.
>>
>> I got a really great idea for this kind of task. I want to share it with you:
>>
>> The case:
>> I got a piece of memory(several Kb), and multiple producers try to put content
>> into that area. A (single) consumer would try to read the content. So there
>> are two kind of synchronization: synchronization between producers and
>> synchronization between consumer and producers.(Note that there is only one
>> consumer, while a lots of producers)
>>
>> I don't want that single consumer to read intermediate data or dirty data so
>> that I need some synchronization mechanism to guarantee that consumer read
>> after a producer have finished putting content into that place. Also, I don't
>> want multiple producers to write to that place at the same time, since that
>> will result in dirty data.
>>
>> Clearly we do not need any lock between consumer and producer, a single flag
>> would suffice. But for synchronization between producers, we need lock. The
>> algorithm can be represented as follow:
>>
>>     //consumer
>>     if (flag == DATA_IS_CLEAN) {
>>         consumer_read_data();
>>         flag = DATA_READED; // set it back so that producer can put
>> content there after
>>     } else {
>>         spin_or_return();
>>     }
>>
>>     // for every producer
>>     if (flag == DATA_READED) { // consumer have finished reading
>>         if (try_lock()) {
>>             put_content();
>>             unlock();
>>         } else {
>>             return;
>>         }
>>     } else {
>>         return;
>>     }
>>
>> so now we have the needed synchronization.
>>
>> But the problem is, a producer can simple die after it does
>> `put_content()` and before
>> `unlock()`. That is awkward, since that effectively means deadlock for
>> that memory area.
>>
>> To rescue, we can use a robust lock from pthread, which guarantees
>> that if the lock
>> holder dies, the next one who tries to acquire the lock will succeed.
>> (see pthread_mutexattr_setrobust(3))
>>
>> Pthread's robust lock is great, and is very fast. However, I still
>> consider it a litte
>> bit heavy and I want to craft my own. I want to make it lock free,
>> with some atomic
>> instruction. So I come up with a idea: I can use a atomic `xadd'
>> instruction for that.
>> I can implement my own `try_lock()' like this:
>>
>>     int try_lock() {
>>         uint8_t previous_value = xadd(&lock, 1);
>>         return previous_value == 0;
>>     }
>>
>> Clearly when *lock == 0, some process can xadd it to 1 and
>> successfully acquire the lock.
>> A correspondingly simple `unlock()' would be:
>>
>>     int unlock() {
>>         *lock = 0;
>>     }
>>
>> Now how do we deal with the deadlock problem? Use wrap around. Because
>> every process who
>> tries to acquire the lock will xadd 1 to the lock, until some time it
>> wrap around and we
>> now reach 0 again, which will atomically unlock the lock!! Therefore,
>> if the lock owner
>> dies, the lock will be unlocked some time.
>>
>> You may ask, what if the lock get wrapped around "too fast" such that
>> two process hold the
>> lock at the same time? The answer is, make the lock of some bigger
>> type, probably uint32_t,
>> in which case you have to have 2^32 processes on a system (at the same
>> time) to complete
>> for that lock, in such a short time(as we are some quick thing inside
>> the critical region).
>> That would make the race impossible. In my case, because I have a long
>> queue, and it takes
>> many processes to occupy a queue to reach the original one, a uint8_t
>> will suffice.
>>
>> (I am designing some lock-free multi-producers single-consumer message
>> queue. The design is
>> pretty nice. I will send more later ;-)
>
> This has been a very popular activity over the past few decades.
> Here are a few places you can look to see other work in this area:
>
> http://libcds.sourceforge.net/
> http://liburcu.org
> https://github.com/concurrencykit/ck
>
> There are quite a few others.
>
> The difficulty with breaking locks is that the state space is quite
> large, and can vary based on things like compiler optimizations.
> Don't get me wrong, people really do things like this, but it is
> not easy.

Thanks, paul.

Yubin
--
To unsubscribe from this list: send the line "unsubscribe perfbook" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux