Hi,

I fear we are getting on the "ever-and-ever-more-complex" track ...

I understand the concurrency issues (how the come into live) but I would love 
if we could solve them with simple code without inventing more and more locking 
and and signaling etc. behaviour. This really was the "beauty of some sorts" of 
the previous lock-less approach.

I think under certain circumstances there will always be a situation where one 
event (service or configuration) is happening while at the same time another 
event is being handled. In some situations -- e.g. a required dependency being 
unregistered while requiring the component is being activated -- the ongoing 
task should probably be aborted and reverted. Which in turn may be simpler 
(before service registration) or harder (during service registration).

This can maybe be solved by using checkpoints: Long running tasks (mostly 
activation) after each step check the checkpoint while intermittent events 
(service, configuration) may set the checkpoint and optionally wait for the 
checkpoint to be confirmed by the long-running task (eventually timing out to 
prevent deadlocks). Such a thing would be "easier" to implement with an 
Activating state where just the Activating.deactivate method might be called 
which would handle the checkpoint thing.

Do I sound getting complicated ? ;-)

Regards
Felix

Am 04.06.2012 um 02:40 schrieb David Jencks:

> A colleague suggested using a read-write lock might work.  I may not 
> understand his proposal: I think he's suggesting a write lock around code 
> that change the state and read lock around code that doesn't.
> 
> I'm not sure how this would help.  I think we'd need the write lock around 
> the code that determines that a bind satisfies a component, downgraded to a 
> read lock while the service is registered.  Then we'd need a write lock on 
> the code that creates the component instance so we only get one instance.
> 
> In the picture below, thread one will publish the service registration inside 
> a read lock on C.  Thread 2 will have a write lock on B since it's trying to 
> create the component instance (B is delayed), blocking thread 1.  In thread 
> 2, B will try to get a write lock on C to create the instance of C (C is also 
> delayed).  (Note that B knows of C because felix ds doesn't use the service 
> events to track which services are available, it uses them to trigger 
> querying for services for binding).
> 
> So here are a couple more lock ideas.
> 
> 1. read-write lock for binding events and state change and a separate plain 
> lock for creating the component instance object.  All the locks in thread 1 
> are read-write, all the locks in thread 2 are plain.  While there's nothing 
> stopping the B service from being unregistered in a third thread while thread 
> 2 is creating the implementation object, at least A will be notified in 
> thread 3 that B is no longer available.  Keeping B consistent could be 
> trickier.
> 
> 2. tracking dependent services using events and never querying for services.  
> This would mean in thread 2 B would not know about C since the event in 
> thread 1 could not be delivered until after the lock on B was released.  I'm 
> not sure how to get an accurate initial list of services with no possibility 
> that we're about to get an event for one of those services.  I guess this is 
> similar to the problem ServiceTracker is supposed to solve.
> 
> I think one of the effects of the non-blocking model I'm thinking of is that 
> for a given component, several service registrations and several service 
> instances may briefly be present.  However the extra ones should disappear 
> quickly as the thread involved discovers it didn't "win".  This is similar to 
> the situation in the 2-locks scenario in (1) above.
> 
> thanks
> david jencks
> 
> 
> 
> 
> On Jun 3, 2012, at 11:21 AM, David Jencks wrote:
> 
>> We found another DS deadlock with the current locking concurrency solution.
>> 
>> When we start, B is registered (its dependencies are optional) but not yet 
>> created (it's delayed)
>> 
>> Thread 1 creates and registers D so service registration and bind attempts 
>> propagate right to left <<<<<< starting with D.  Thread 1 obtains the lock 
>> on C and tries to get the lock on B
>> 
>>    A  ---- 1..1 -----> B -------0..n -------> C  --------- 1..1 ------->D
>> immediate         delayed                 delayed                       not 
>> a ds service
>> 
>> Thread 2 going left to right >>>>>>> creates A which calls getService on B 
>> (obtaining the B lock) which calls getService on C which is locked by thread 
>> 1.
>> 
>> I'm considering two possible approaches:
>> 
>> 1. dont lock or don't lock as much for events that don't change the state 
>> (the C to B service event: B can't change state as a result)
>> 
>> 2. a lock-free implementation using compare-and-set.  The idea is that an 
>> event such as a service event would get processed using a holder.  If at the 
>> completion of the processing the previous holder is the same then we'd 
>> compare-and-set the holder, otherwise undo whatever happened during 
>> processing.
>> 
>> (2) could result in multiple service registrations for a given component 
>> while the events are getting processed.  It's not completely obvious to me 
>> how to assure that all events will in fact get processed and that e.g the 
>> "winning" result will have all the highest-priority service references and 
>> that there won't be infinite update cycles.
>> 
>> On the other hand I have no confidence that (1) is possible to implement.  
>> So I'm going to try for (2).  This is going to reopen the "how does it run 
>> on java < 5" debate since compare-and-set is not available there with the 
>> same characteristics of the java 5 implementation.
>> 
>> In the interests of making more code paths more consistent and similar I 
>> think I will change the immediate component handling to be as the spec 
>> describes, where the service is registered before the implementation object 
>> is created.  This means all the services will be registered as service 
>> factories as the delayed components currently are.
>> 
>> In other DS issues:
>> 
>> I'd like to separate the felix specific non-spec ComponentFactory behavior 
>> (where it acts like a ManagedServiceFactory) into a separate class so I can 
>> understand the spec compliant behavior more easily.
>> 
>> I think that the behavior of ServiceFactory components that are configured 
>> by config admin is wrong.  AFAICT for service factory components we will 
>> never call a modify method but always destroy and recreate the instances.
>> 
>> I also have a few cleanup changes such as better javadoc and removing stray 
>> references to no-longer-present *ing transient states that I don't think 
>> will be controversial so I plan to just commit them.
>> 
>> thanks
>> david jencks
>> 
> 

Reply via email to