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> 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
> 

_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to