You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@felix.apache.org by "Pierre De Rop (JIRA)" <ji...@apache.org> on 2017/01/04 21:57:58 UTC

[jira] [Comment Edited] (FELIX-5471) Ensure that unbound services are always handled synchronously

    [ https://issues.apache.org/jira/browse/FELIX-5471?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15799473#comment-15799473 ] 

Pierre De Rop edited comment on FELIX-5471 at 1/4/17 9:57 PM:
--------------------------------------------------------------

Hi Jeroen,

I have committed a patch in rv 1777378 in ComponentImpl class in order to clarify the javadoc for the schedule method, and I renamed the "trySynchronous" argument of the schedule method to "bypassThreadPoolIfPossible" in order to make the method less confusing. So if there is no threadpool, the flag is ignored and then the default serial queue executor is used (the task is scheduled synchronously if the queue is not currently being run by another master thread). If now a threadpool is used and if bypassThreadPoolIfPossible is true, then the task is also run synchronously if the queue is not currently being run from the threadpool.

So, whether or not a ComponentExecutorFactory is used, we always try to handle removed service events synchronously if the component is not currently handling a service dependency (if its queue is idle, not busy).

Now let's try to clarify so you can estimate if you may or may not have the issue (whether or not you use a ComponentExecutorFactory):

* First let's recap the issue: sometimes, when a service X is being unregistered, then the other services (M for example) depending on X may not be called in M.unbind(X) synchronously while X is being unregistered. So X may then be stopped at a point where M.unbind(X) is not yet called (but will be called eventually, soon).

* When the issue does not happen ? you won't have the ordering issue if you stop your components synchronously from a single thread and after all components have been started (like it is the case for example when the framework is shutting down or when you manually stop a bundle from the gogo shell).

* When the issue may happen ? you may have the issue if you concurrently add *and* remove some components at the same time. For example you may have the issue in the following use case (whether or not you use concurrent DM):
** X, and Y are available, and M optionally depends on X, Y.
** you add M from thread T1
** then concurrently, you remove X from thread T2

So, using the scenario above,  it may happen that M.unbind(X) is called after X.stop().
This is because we have implemented the thread model in a non blocking way, using queues.

So to summarize: if you manage to stop components from a single thread  and at a point where components are all fully started, then there is no issue.

Now, I also added two concurrent test cases:

- ServiceRaceWithOrderedUnbindTest.java: this junit test does not use DM in concurrent mode (no ComponentExecutorFactory is used, as it is the case in default DM). So, the test uses manually created threads in order to perform concurrent component creations. Then, the components are unregistered from a single thread. And the test verifies if the unbound methods are called synchronously while the lost service dependencies are being unregistered.

- ServiceRaceParallelWithOrderedUnbindTest.java: same test as above, but this time we are using a ComponentExecutorFactory (concurrent DM is used). 

So, are you stopping components while some other components are starting concurrently ?

thank you.


was (Author: pderop):
Hi Jeroen,

I have committed a patch in rv 1777378 in ComponentImpl class in order to clarify the javadoc for the schedule method, and I renamed the "trySynchronous" argument of the schedule method to "bypassThreadPoolIfPossible" in order to make the method less confusing. So if there is no threadpool, the flag is ignored and then the default serial queue executor is used (the task is scheduled synchronously if the queue is not currently being run by another master thread). If now a threadpool is used and if bypassThreadPoolIfPossible is true, then the task is also run synchronously if the queue is not currently being run from the threadpool.

So, whether or not a ComponentExecutorFactory is used, we always try to handle removed service events synchronously if the component is not currently handling a service dependency (if its queue is idle, not busy).

Now let's try to clarify so you can estimate if you may or may not have the issue (whether or not you use a ComponentExecutorFactory):

* First let's recap the issue: sometimes, when a service X is being unregistered, then the other services (M for example) depending on X may not be called in M.unbind(X) synchronously while X is being unregistered. So X may then be stopped at a point where M.unbind(X) is not yet called (but will be called eventually, soon).

* When the issue does not happen ? you won't have the ordering issue if you stop your components synchronously from a single thread and after all components have been started (like it is the case for example when the framework is shutting down or when you manually stop a bundle from the gogo shell).

* When the issue may happen ? you may have the issue if you concurrently add *and* remove some components at the same time. For example you may have the issue in the following use case (whether or not you use concurrent DM):
** X, and Y are available, and M optionally depends on X, Y.
** you add M from thread T1
** then concurrently, you remove X from thread T2

So, using the scenario above,  it may happen that M.unbind(X) is called after X.stop().
This is because we have implemented the thread model in a non blocking way, using queues.

So to summarize: if you manage to stop components from a single thread  and at a point where components are all fully started, then there is no issue.

Now, I also added two concurrent test cases:

- ServiceRaceWithOrderedUnbindTest.java: this junit test does not use DM in concurrent mode (no ComponentExecutorFactory is used, as it is the case in default DM). So, the test uses manually created threads in order to perform concurrent component creations. Then, the components are unregistered from a single thread. And the test verifies if the unbound methods are called synchronously while the lost service dependencies are being unregistered.

- ServiceRaceParallelWithOrderedUnbindTest.java: same test as above, but this time we are using a ComponentExecutorFactory (concurrent DM is used). 

So, are stopping components while some other components are starting ?

thank you.

> Ensure that unbound services are always handled synchronously
> -------------------------------------------------------------
>
>                 Key: FELIX-5471
>                 URL: https://issues.apache.org/jira/browse/FELIX-5471
>             Project: Felix
>          Issue Type: Bug
>          Components: Dependency Manager
>    Affects Versions: org.apache.felix.dependencymanager-r1
>            Reporter: Pierre De Rop
>            Assignee: Pierre De Rop
>             Fix For: org.apache.felix.dependencymanager-r9
>
>
> When a component loses a service dependency, it should handle the lost service synchronously. For example, if service A loses a dependency on B (because B is being unregistered),  then A.remove(B) should be called synchronously (when B is being unregistered from the service registry), else the A.remove(B) callback could possibly be invoked while B is already unregistered and stopped.
> Currently, unbound services may be handled asynchronously if DM is used in a concurrent mode (using a threadpool). And even if no threadpool is used, the issue may happen if there is a highly concurrent situation where services are registered/removed concurrently from multiple threads.
> So, a patch should be done in order to ensure that a service dependency remove event is always handled synchronously (especially if DM is used with a threadpool).
> I will provide a testcase soon.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)