We've run into one definite concurrency problem in SCR and I've been discussing 
offline with a colleague how to fix it and wanted to get the discussion out in 
the open.

The original symptom was when 2 mandatory service refs were satisfied on 
different threads at once: the 2nd wasn't recognized so the component never got 
activated.

This is easily solved by synchronizing but this introduces risk of deadlocks 
(my first attempt, 
https://issues.apache.org/jira/secure/attachment/12522537/FELIX-3456-1.diff)

We tried some partly asynchronous approaches such as 
https://issues.apache.org/jira/secure/attachment/12523313/FELIX-3456-4.diff.  
Unless there's a timeout (presumably due to deadlock) this gets all service 
events processed before the thread exits from its first call into SCR.  However 
this can result in service events getting processed later than one expects 
possibly on a different thread.  On further thought we concluded that a service 
event must be processed fully before the service registration call returns.  We 
therefore don't think any kind of asynchronous approach will work.

We've discovered the anti-circular-dependency clause in the spec (112.3.5) but 
it appears to be overly biased towards SCR-only graphs of services.  We are 
leaning towards thinking that SCR also needs to consider:

- an activate method registers a service that satisfies an optional dependency 
of a component being activated by scr on the same thread.
- the same, except the activate method starts a new thread to register the 
service and waits for it to complete.

Another scenario to consider is

components C1 and C2 registering as services, each with an optional dynamic 
dependency on the other.  If one starts, and then the other, there is no 
problem, they both get references to the other.  If they both start at the same 
time in separate threads (either because they are in different bundles or 
because they get activated due to mandatory references being satisfied) and 
register the services while the other is in the Activating state, a simple lock 
over the service event processing will result in deadlock.  Furthermore, to get 
the correct result, at least one of the services has to be bound while the 
component to which is is binding is in the Activating state.

It looks like the situation can be simplified a bit by considering, for service 
events, whether the dependency will result in a state change: if it's optional 
or mandatory but not the only satisfying service, it won't, but if it's 
mandatory and the first satisfying service, it will.  We can calculate this 
before calling any bind methods or activate methods.  After determining this, 
we know the final state of the component.

We're considering whether some kind of 2-stage lock would work:

one level can change the state and blocks all other threads
the other level can't change the state and lets stuff like service events for 
non-state-changing service references be processed according to the final state 
of the component. (e.g. activating will let bind methods be called on the 
under-configuration object).

This does not yet consider bundle event driven state changes or deactivation or 
delayed component creation or service factories.

Comments and more scenarios to consider are more than welcome.

thanks
david jencks



Reply via email to