• No results found

6. Concurrency and OSGi

6.5. GUI Development

other bundles, we simply cannot enforce any such ordering, because those bundles cannot know about our ordering scheme. The only alternative is to restructure our code to avoid holding any locks when calling the OSGi APIs. Note that avoiding locks does not just mean avoiding thesynchronizedkey- word. We could achieve the level of isolation we desire in theregisterMail- boxmethod by using a binary semaphore or an instance of the Lockclasses. However when used in such a way, these constructs have exactly the same se- mantics as asynchronizedmethod or block, and can result in the same types of deadlock.

The trick is avoid holding locks during calls to OSGi APIs, but to do thiswith- out losing the atomicity guarantees that we require. Sometimes this requires some re-ordering of the operations. For example, Listing6.12shows a version of the service that uses a lock only to manipulate themapfield4. The result of the operation passes out of the locked region and tells us whether there was a prior registration of the service which needs to be unregistered. Note that the putmethod of Mapreturns the previous mapping for the key if one existed. Due to the reordering there is a slight change in the externally observable behaviour of this method. The previous version (if it worked!) would result in a short gap between the old service being removed and the new service being created, during which there would be no service available. The new version reverses the steps: the new service is created very slightly beforethe old service is removed, so there will briefly be two services rather than none. It turns out this is not such a bad thing: as well as making it possible to reduce the size of our locked regions, consumers of the service also benefit from having a replacement service immediately available when the first service goes away.

6.5. GUI Development

Another area where multi-threading causes pain is in programming graphical user interfaces (GUIs). Almost all GUI libraries — including the most popular Java ones, Swing and SWT — insist that all calls to those libraries must be made from a single thread, the “event dispatch thread” or EDT. But as we have seen, when our callbacks or service methods are executed, we have no idea whether we are in the EDT or some other, arbitrary thread. Therefore we have to use utilities supplied by the GUI library to pass blocks of code that will be executed in the EDT when it gets around to it. In Swing, we need to pass a Runnable instance to the SwingUtilities.invokeLater method, but for efficiency, we should first check whether we’re already in the EDT by calling EventQueue.isDispatchThread

4In this example we use a plainHashMapand wrap the calls to it in asynchronizedblock in

order to be explicit about the locking. We could have used aConcurrentHashMapwhich performs fine-grained internal locking with no need for asynchronizedblock.

Listing 6.12Avoiding holding a lock while calling OSGi APIs 1 p a c k a g e o r g.o s g i.b o o k.c o n c u r r e n c y; 3 i m p o r t j a v a.u t i l.H a s h M a p; 4 i m p o r t j a v a.u t i l.M a p; 5 i m p o r t j a v a.u t i l.P r o p e r t i e s; 7 i m p o r t o r g.o s g i.b o o k.r e a d e r.a p i.M a i l b o x; 8 i m p o r t o r g.o s g i.f r a m e w o r k.B u n d l e C o n t e x t; 9 i m p o r t o r g.o s g i.f r a m e w o r k.S e r v i c e R e g i s t r a t i o n; 11 p u b l i c c l a s s G o o d L o c k i n g M a i l b o x R e g i s t r a t i o n S e r v i c e i m p l e m e n t s 12 M a i l b o x R e g i s t r a t i o n S e r v i c e { 14 p r i v a t e f i n a l M a p<S t r i n g, S e r v i c e R e g i s t r a t i o n> m a p 15 = n e w H a s h M a p<S t r i n g, S e r v i c e R e g i s t r a t i o n>(); 17 p r i v a t e f i n a l B u n d l e C o n t e x t c o n t e x t; 19 p u b l i c G o o d L o c k i n g M a i l b o x R e g i s t r a t i o n S e r v i c e(B u n d l e C o n t e x t c o n t e x t) { 20 t h i s.c o n t e x t = c o n t e x t; 21 } 23 p u b l i c v o i d r e g i s t e r M a i l b o x(S t r i n g n a m e, M a i l b o x m a i l b o x) { 24 P r o p e r t i e s p r o p s = n e w P r o p e r t i e s( ) ; 25 p r o p s.p u t(M a i l b o x.N A M E _ P R O P E R T Y, n a m e) ; 26 S e r v i c e R e g i s t r a t i o n r e g = c o n t e x t.r e g i s t e r S e r v i c e( 27 M a i l b o x.c l a s s.g e t N a m e( ) , m a i l b o x, p r o p s) ; 29 S e r v i c e R e g i s t r a t i o n p r i o r R e g; 30 s y n c h r o n i z e d (m a p) { 31 p r i o r R e g = m a p.p u t(n a m e, r e g) ; 32 } 34 i f (p r i o r R e g != n u l l) { 35 p r i o r R e g.u n r e g i s t e r( ) ; 36 } 37 } 38 }