Yup, it got a bit windy ;-) I put it on my website as a blog since I've no good other place at the moment.
http://aqute.biz/2018/08/02/the-service-window.html <http://aqute.biz/2018/08/02/the-service-window.html> Let me know if things are unclear. Kind regards, Peter Kriens > On 2 Aug 2018, at 11:58, David Leangen via osgi-dev <osgi-dev@mail.osgi.org> > wrote: > > > Wow! That is a lot to digest. > > I’ll need to get back to you in a few days/weeks/months/years. :-D > > Thanks so much!! > > > Cheers, > =David > > > > >> On Aug 2, 2018, at 18:38, Peter Kriens <peter.kri...@aqute.biz >> <mailto:peter.kri...@aqute.biz>> wrote: >> >> >> ## Keep Passing the Open Windows >> >> You did read the classic [v2Archive OSGi enRoute App note][5] about this >> topic? It has been archived by the OSGi to [v2Archive OSGi enRoute web >> site][3]. It handles a lot of similar cases. There is an accompanying >> workspace [v2Archive OSGi enRoute osgi.enroute.examples.concurrency >> <https://github.com/osgi/osgi.enroute.examples.concurrency>][7] >> >> Anyway, I am not sure if you want to solve this pragmatic or pure? >> >> ## Pragmatic >> >> Pragmatic means there is a tiny chance you hit the window where you check if >> the MyService is unregistered and then use it. If you're really unlucky you >> just hit the unregistration after you checked it but before you can use it. >> It works when the unregistration of MyService is rare and the work is long. >> Yes, it can fail but so can anything so you should be prepared for it. >> >> Pragmatic works best as follows: >> >> @Component >> public class MyClass extends Thread { >> @Reference MyService myService; >> >> @Activate void activate() { start(); } >> @Deactivate void deactivate() { interrupt(); } >> >> public void run() { >> while (!isInterrupted()) { >> try { >> MyResult result = doHardWork(); >> if (!isInterrupted()) >> myService.setResult(result); >> } catch (Exception e) { /* TODO */ } >> } >> } >> } >> >> Clearly there is a race condition. >> >> >> >> >> ## Pure >> >> I once had a use case where we had whiteboard listeners that received >> events. The frequency and some not so good event listeners that took too >> much time in their callback. This created a quite long window where it could >> fail so it often did. For that use case I created a special highly optimized >> class that could delay the removal of the listener while it was being >> dispatched. To make it have absolutely minimal overhead was tricky, I even >> made an Alloy model of it that found some design errors. Anyway, sometimes >> you have pick one of the bad sides, this was one where delaying the >> deactivate was worth it. >> >> So how would you make this 'purer' by delaying the deactivation until you >> stopped using it? Since the service is still supposed to be valid during >> deactivate we could make the setResult() and the deactivate() methods >> exclude each other. That is, we need to make sure that no interrupt can >> happen when we check for the isInterrupted() and call myService.setResult(). >> We could use heavy locks but synchronized works fine for me when you realize >> some of its caveats: >> >> * Short blocks >> * Ensure you cannot create deadlocks >> >> So there must be an explicit contract that the MyService is not going to >> stay away for a long time nor call lots of other unknown code that could >> cause deadlocks. After all, we're blocking the deactivate() method which is >> very bad practice in general. So you will trade off one purity for another. >> >> @Component >> public class MyClass extends Thread { >> @Reference MyService myService; >> >> @Activate void activate() { start(); } >> @Deactivate synchronized void deactivate() { interrupt(); } >> >> public void run() { >> while (!isInterrupted()) { >> try { >> MyResult result = doHardWork(); >> synchronized(this) { >> if (!isInterrupted()) { >> myService.setResult(result); >> } >> } >> } catch (Exception e) { /* TODO */ } >> } >> } >> } >> >> This guarantees what you want … However (you knew this was coming!) there is >> a reason the service gets deactivated. Even though the _service_ is still >> valid at that point, there is a reason the _service object_ indicated its >> unwillingness to play. For example, if MyService was remoted then the >> connection might have been lost. In general, when you call a service you >> should be prepared that it fails. (That is why you should always take >> exceptions into account even if they're not checked.) >> >> ## Better API >> >> The best solution is usually to turn the problem around. This clearly can >> only happen when you can influence the API so that is often not a choice. If >> you can, you can pass a Promise to the myService and calculate in the >> background. Clearly that means you keep churning doing the hard work. Unless >> the calculation is very expensive and the unregistration happens often, >> doing the calculation unnecessary should normally have no practical >> concerns. If it is, you might want to consider CompletableFuture instead of >> Promise since it has a cancel() method. (We rejected a cancel since it makes >> the Promise mutable, but admittedly it is useful. However, it has the same >> race issues as we discuss here.) >> >> @Component >> public class MyClass { >> >> @Reference MyService myService; >> @Reference PromiseFactory promiseFactory; >> >> @Activate void activate() { >> Promise<MyResult> result = promiseFactory.submit( this::doHardWork ); >> myService.setResult( result ); >> } >> } >> >> This is an example where you see a very weird effect that I first noticed in >> the eighties during my first big OO design. At first you think the problem >> is now moved from MyClass to MyService? I think when you try to implement >> this that you find that the problem mostly _disappeared_. During one of the >> first large systems I designed I kept feeling we were kicking the hard >> problems down the road and we still run into a brick wall. However, one day >> we realized we were done. For some reason the hard problems were solved in >> the structure of the application and not in specific code. Weird. However, >> realizing this I often have to cry a bit when I realize how some designs are >> doing the opposite and make simple things complex :-( >> >> ## Multiple Results >> >> If you have multiple results to deliver you might want to take a look at the >> [OSGi PushStream][1]. When I made the initial design for ASyncStreams (feels >> eons ago :-( ) that inspired the OSGi Push Stream specification this was >> one of the use cases I had in mind. The Push Stream are intended to handle >> all the nasty cases and shield you from them. As a bonus, it actually works >> for multiple receivers as well. Push Streams provide a simple low cost >> backlink to handle the case where the MyService gets closed. Haven't looked >> at where Push Stream's ended up but as far as I know they should still be >> useful when your hard work delivers multiple results. Ah well, I wanted to >> take a look at it anyway since it has been released now. Let's see how that >> would look like: >> >> @Component >> public class ProviderImpl extends Thread { >> >> @Reference PushStreamProvider psp; >> @Reference MyService myService; >> >> volatile SimplePushEventSource<MyResult> dispatcher; >> >> @Activate void activate() throws Exception { >> dispatcher = >> psp.createSimpleEventSource(MyResult.class); >> myService.setResult(dispatcher); >> start(); >> } >> >> @Deactivate void deactivate() { >> interrupt(); >> } >> >> @Override >> public void run() { >> try { >> MyResult r = doHardWork(); >> while (!isInterrupted()) { >> dispatcher.publish(r); >> r = doHardWork(); >> } >> } finally { >> dispatcher.close(); >> } >> } >> } >> >> ## Use of Executors >> >> As a side note. I've been in systems where everybody was mucking around with >> ExecutorServices and it became a mess. In [v2Archive OSGi enRoute][3] I >> always provided an [Executor service][4] that is shared and does proper >> cleanup when the service getter goes away. (The [v2Archive OSGi enRoute >> Scheduler][6] was also very nice for this since it provides Cancelable >> Promises.) Executor Services created statically are horror in OSGi since >> they are completely oblivious of the OSGi dynamics. And in your case they >> are totally unnecessary. The only utility they provide to you is that they >> interrupt the threads. This is trivial to do when you create your own >> thread. (And messages about the expensiveness of threads are highly >> exaggerated.) Even if you use an Executor you can pass the thread. >> >> Deferred<Thread> deferred = new Deferred<>(); >> Promise<MyResult> promiseFactory.submit( () -> { >> deferred.resolve( Thread.currentThread() ); >> >> while ( result == null && !Thread >> <http://thread.is/>.currentThread().isInterrupted() { >> … do some hard work >> } >> return result; >> }); >> >> // deactivate >> deferred.getPromise().getValue().interrupt(); >> >> In general, if you go this route, suggest you clearly separate the >> strategies from the code. I.e. make a separate class to capture the strategy >> of handling these things. Worst designs are where these are mixed. >> >> ## Disclaimer >> >> I guess this became a tad long, I guess I will turn it into a blog. >> >> Anyway, usually disclaimer: none of the code has been tested so use it at >> your own peril! >> >> Good luck, kind regards, >> >> Peter Kriens >> >> [1]: https://osgi.org/specification/osgi.cmpn/7.0.0/util.pushstream.html >> <https://osgi.org/specification/osgi.cmpn/7.0.0/util.pushstream.html> >> [2]: >> http://www.plantuml.com/plantuml/png/RP2n2i8m48RtF4NST6WVe4Cj24M7Ka71EII71jjKxYwLlhsXMXghO-w3Z-zFGQoGVTk8QZW1zbQ3J79PNcGc4QwM6524LxXLmwvHH07epX6Zr_mcCo1WsKwU9LIQRQyOn7GAplCDGPa0nmoHfgdud69ekhr2y-pm_ezQEZW6HFzWCDlHyRl5ksXDN6LWsPNaiteIhpUBjk_D2EGRZeVD1PayrdMv4WKu4_xv1G00 >> >> <http://www.plantuml.com/plantuml/png/RP2n2i8m48RtF4NST6WVe4Cj24M7Ka71EII71jjKxYwLlhsXMXghO-w3Z-zFGQoGVTk8QZW1zbQ3J79PNcGc4QwM6524LxXLmwvHH07epX6Zr_mcCo1WsKwU9LIQRQyOn7GAplCDGPa0nmoHfgdud69ekhr2y-pm_ezQEZW6HFzWCDlHyRl5ksXDN6LWsPNaiteIhpUBjk_D2EGRZeVD1PayrdMv4WKu4_xv1G00> >> [3]: https://v2archive.enroute.osgi.org/ >> <https://v2archive.enroute.osgi.org/> >> [4]: >> https://github.com/osgi/v2archive.osgi.enroute/tree/master/osgi.enroute.executor.simple.provider >> >> <https://github.com/osgi/v2archive.osgi.enroute/tree/master/osgi.enroute.executor.simple.provider> >> [5]: https://v2archive.enroute.osgi.org/appnotes/concurrency.html >> <https://v2archive.enroute.osgi.org/appnotes/concurrency.html> >> [6]: >> https://github.com/osgi/v2archive.osgi.enroute/tree/master/osgi.enroute.scheduler.simple.provider >> >> <https://github.com/osgi/v2archive.osgi.enroute/tree/master/osgi.enroute.scheduler.simple.provider> >> [7]: https://github.com/osgi/osgi.enroute.examples.concurrency >> <https://github.com/osgi/osgi.enroute.examples.concurrency> >> >>> >>> On 2 Aug 2018, at 02:01, David Leangen via osgi-dev <osgi-dev@mail.osgi.org >>> <mailto:osgi-dev@mail.osgi.org>> wrote: >>> >>> >>> Hi Tim, >>> >>> Thanks, and this is good advice. The example you give is when the thread is >>> in the same component that is being deactivated. In this case, as you show, >>> it is quite trivial to track the activation state of the component in order >>> to shut down the thread. >>> >>> In my case, the trouble I am having is that the long-running thread is in a >>> component that is different from the component that is getting deactivated. >>> For instance, building on your example: >>> >>> @Component >>> public class MyClass { >>> >>> // Note that I am using a STATIC service >>> @Reference private MyService myService; >>> >>> private final AtomicBoolean closed = new AtomicBoolean(); >>> >>> @Activate >>> void start() { >>> new Thread(this::longStart).run() >>> } >>> >>> >>> @Deactivate >>> void stop() { >>> closed.set(true); >>> } >>> >>> void longStart() { >>> for(int i = 0; i < 1000000; i++) { >>> >>> // This only works if the service object is not stateful, >>> otherwise we need >>> // to do a check and throw away an intermediate invalidated >>> result >>> >>> // Understood, but unfortunately the service object is stateful. >>> >>> // The problem is that the dependency can be deactivated at any >>> time, and this >>> // is happening before “closed" in this component get set to >>> “true". I do not know how >>> // to detect the deactivation of the dependency. I need to >>> determine this pre-emptively, >>> // not after-the-fact. Otherwise the result will be destructive. >>> >>> doSomethingWithMyService(myService); >>> >>> // Ideally I would like to do something like this: >>> if (myServiceIsStillActive()) >>> doSomethingWithMyService(myService); >>> } >>> } >>> } >>> >>> In the second example, there is a dynamic @Reference, so I see the point of >>> using an AtomicReference. However, I am using a static @Reference, so I >>> doubt that just putting in an AtomicReference will change the timing >>> problem. >>> >>> Any thoughts? >>> >>> >>> >>> By the way, instead of using a “closed” variable, I am doing something like >>> this: >>> >>> @Activate >>> void activate() >>> { >>> executor = Executors.newSingleThreadExecutor(); >>> } >>> >>> void deactivate() >>> { >>> executor.shutdownNow(); >>> } >>> >>> Then I only need to test for Thread.interrupted(). I assume this has the >>> same effect as having the check for “closed". >>> >>> Cheers, >>> =David >>> >>> >>> >>>> On Aug 1, 2018, at 16:59, Tim Ward <tim.w...@paremus.com >>>> <mailto:tim.w...@paremus.com>> wrote: >>>> >>>> Hi David, >>>> >>>> In addition to interrupting the worker thread (which is a good idea). >>>> There are a couple of useful things that you can do using the support from >>>> java.util.concurrent. For example, setting a closed state: >>>> >>>> >>>> @Component >>>> public class MyClass { >>>> >>>> private final AtomicBoolean closed = new AtomicBoolean(); >>>> >>>> @Activate >>>> void start() { >>>> new Thread(this::longStart).run() >>>> } >>>> >>>> >>>> @Deactivate >>>> void stop() { >>>> closed.set(true); >>>> } >>>> >>>> void longStart() { >>>> for(int i = 0; i < 1000000; i++) { >>>> if(closed.get()) { >>>> break; >>>> } >>>> doSomething(); >>>> } >>>> } >>>> } >>>> >>>> Also if your references are dynamic then you should treat them carefully >>>> >>>> @Component >>>> public class MyClass implements MySlowService { >>>> >>>> private final AtomicReference<MyService> myRef = new >>>> AtomicReference<>(); >>>> >>>> @Reference(policy=DYNAMIC) >>>> void setReference(MyService service) { >>>> myRef.set(service) >>>> } >>>> >>>> void unsetReference(MyService service) { >>>> // Note that it is *not* safe to just do a set null, see Compendium >>>> 112.5.12 >>>> myRef.compareAndSet(service, null); >>>> } >>>> >>>> public void longRunningTask() { >>>> for(int i = 0; i < 1000000; i++) { >>>> // This only works if the service object is not stateful, >>>> otherwise we need >>>> // to do a check and throw away an intermediate invalidated >>>> result >>>> >>>> MyService myService = myRef.get(); >>>> doSomethingWithMyService(myService); >>>> } >>>> } >>>> } >>>> >>>> I hope you find these helpful. >>>> >>>> Tim >>>> >>>>> On 1 Aug 2018, at 05:44, David Leangen via osgi-dev >>>>> <osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org>> wrote: >>>>> >>>>> >>>>> Hi! >>>>> >>>>> I am running into a situation where, what I think is happening is: >>>>> >>>>> Component A gets instantiated >>>>> Component B >>>>> - references A >>>>> - gets satisfied once A is satisfied >>>>> - kicks off a long-running process when one of its methods are called >>>>> - the long-running process is run in a different thread, with a Promise >>>>> Component A is no longer satisfied >>>>> But >>>>> - the long-running process is still running >>>>> - the long-running process now references an invalid Component A >>>>> - the long-running thread fails because of the invalid state of Component >>>>> A >>>>> Component B is no longer satisfied >>>>> >>>>> >>>>> So, the long-running component messes things up, but its component has >>>>> not yet shut down even though its process is still happily running in >>>>> another thread. >>>>> >>>>> I can think of two possible solutions, but not sure which is best and not >>>>> sure how to implement: >>>>> >>>>> 1) Figure out a way to share an ExecutorService between “related” >>>>> components so that when one component >>>>> shuts down it will signal to the other related components that their >>>>> threads are now interrupted >>>>> >>>>> 2) In the long-running process, determine if the component that provides >>>>> the required service >>>>> is still active before continuing with the havoc-wreaking process >>>>> >>>>> >>>>> Does this sound about right? >>>>> >>>>> How would I actually accomplish either of these? >>>>> >>>>> >>>>> Thanks! >>>>> =David >>>>> >>>>> >>>>> _______________________________________________ >>>>> OSGi Developer Mail List >>>>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org> >>>>> https://mail.osgi.org/mailman/listinfo/osgi-dev >>>>> <https://mail.osgi.org/mailman/listinfo/osgi-dev> >>>> >>> >>> _______________________________________________ >>> OSGi Developer Mail List >>> osgi-dev@mail.osgi.org <mailto:osgi-dev@mail.osgi.org> >>> https://mail.osgi.org/mailman/listinfo/osgi-dev >>> <https://mail.osgi.org/mailman/listinfo/osgi-dev> > > _______________________________________________ > OSGi Developer Mail List > osgi-dev@mail.osgi.org > https://mail.osgi.org/mailman/listinfo/osgi-dev
_______________________________________________ OSGi Developer Mail List osgi-dev@mail.osgi.org https://mail.osgi.org/mailman/listinfo/osgi-dev