Hi Dirk Thanks for bringing up this discussion. I find it educational.
On Tue, May 2, 2017 at 10:27 PM, Dirk Hogan <dirk.ho...@forgerock.com> wrote: > Hi Neil- > OK - now we are getting somewhere. > > You need to dig a bit deeper to see what establishes a happens-before > relationship. For within a single thread, it is the Program Order: the > underlying virtual machine cannot allow the visibility of memory effects > which contradict the sequence of instructions defined in your .java file, > even though these commands will be re-ordered by the compiler, and > pipelined and executed concurrently by machine cores. > > Between threads, it is the Synchronization Order, which is NOT Program > Order. Between threads, the memory effects of your .java file writes/reads > do NOT have to be honored. So if you say 'But Felix writes the reference > before publishing the service', and thus concluding that the reference will > always be present in the service, you are correct, but ONLY from the > perspective of the thread which wrote the reference and published the > service. For ALL other threads, all bets are off, unless there is a > Synchronization Action (volatile reference read/write, monitor lock/unlock > on the same monitor) which dictates a Synchronization Order of the > visibility of memory effects between DISTINCT threads. I read some of your references and according to my understanding you are disregarding the transitive nature of different orderings here. Let me explain: - assume we have a component C with a field f (annotated with @Reference) - assume C is a delayed component providing a service - assume the component is satisfied and its corresponding ServiceFactory sf is registered with the service-registry - assume no other thread has requested the service before - thread T1 requests C from the service registry - thread T2 requests C from the service registry (assume T1 wins the race) - T2: waits for a CountDownLatch (that's how it's implemented in Felix) - T1: SCR creates an instance of C called c - T1: SCR injects @Reference fields, i.e. it sets c.f - T1: SCR calls c.activate() - T1: c is published by setting it in a volatile field of the ServiceFactory: sf.componentInstance - T1: "releases" the CountDownLatch allowing T2 to proceed - T2: reads c from sf.componentInstance - T2: reads c.f All actions that happen in T1 are governed by Program Order. Setting sf.componentInstance is visible across threads due to Synchronization Order (it is a volatile field). Because actions in T1 are bound by PO, the field sf.componentInstance is set AFTER c.f is set. T2 can only get a reference to c AFTER sf.componentInstance is set. Therefore, transitively, T2 can only get a reference to c AFTER c.f is set. This is in line with my understanding of the following quote from https://shipilev.net/blog/2014/jmm-pragmatics/, at the end of the section "Happens-Before: Publication": "Release can be thought of as the operation which releases all the preceding state updates into the wild, and acquire is the paired operation which receives those updates. So, after a successful acquire, we can see all the updates preceding the paired release." Regards Julian > > The definition of Synchronization Actions, how they combine to create a > Synchronization Order, and how this, in turn, allows a happens-before > relationship to be established, is a formal set of rules defined in the > JMM. There is no ambiguity here. A read of section 17.4 will illuminate as > much. It is very dense - that is why I turned to the other references I > provided in the links above. They are not a quick/easy read, but are > necessary to coherently reason about memory visibility across threads. > > Thanks > > Dirk > > On Tue, May 2, 2017 at 1:10 PM, Neil Bartlett <njbartl...@gmail.com> wrote: > >> I think I get what Dirk is saying, and although I disagree with some of >> his analysis there is arguably a gap in the DS specification that makes >> this unclear. I hope somebody can point at the vital sentence I have >> missed, but here is my analysis: >> >> The JLS defines happens-before thus: "Two actions can be ordered by a >> happens-before relationship. If one action happens-before another, then the >> first is visible to and ordered before the second.” ( >> https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5 < >> https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5 >> >). >> >> Define two actions, A1 and A2. A1 is the setting of an @Reference field in >> a component. A2 is reading the value of that field. It is Dirk’s contention >> that no provable happens-before relationship edge exists between A1 and A2, >> and therefore it’s possible for A2 to access the value of the @Reference >> field that obtained before A1. Let’s see. >> >> A1 is an action that is taken by SCR on an object that it creates (the >> component instance). A2 can only occur in the following circumstances: >> >> 1. SCR itself reads the field. >> SCR is in full control of its own code and I am assuming a correct SCR >> implementation. >> >> 2. The activate method of the component uses the value of the field. >> Section 112.3.8.1 of OSGi Compendium R6 states "there is a happens-before >> relationship between setting the field and activating the component >> instance”. Also section 112.3.6.1 states “The bind method is called and/or >> the field is set before the component instance is activated”, which is less >> clear about the happens-before but I think the intent is there, and it also >> clarifies that this applies to bind methods as well as injected fields. >> Therefore action A1 happens-before A2 in this case. >> >> Note that the spec does not tell the implementation HOW to achieve this >> happens-before edge. As Dirk points out, if it is done with a synchronized >> block then it would have to be on the same monitor. For example, the >> implementation can achieve this by synchronizing on the component instance >> itself when binding fields and calling the activate method. >> >> 3. The activate method starts a thread which uses the value of the field >> (I think this maps to Dirk’s HTTP-serving example). >> From point 2 above, we know that A1 happens-before the activate method is >> invoked. JLS 17.4.5 states that “a call to start() on a thread >> happens-before any actions in the started thread”. JLS also states that >> happens-before is transitive, i.e. if X happens-before Y and Y >> happens-before Z then X happens-before Z. >> >> Therefore A1 happens-before A2 in this case. >> >> 4. Service method(s) on the component use the value of the field. >> The analysis here diverges for immediate vs delayed components. >> >> For immediate components, Compendium section 112.5.3 states: "If an >> immediate component configuration is satisfied and specifies a service, SCR >> must register the component configuration as a service in the service >> registry and then activate the component configuration”. This does NOT >> state that the registration of the service must happen after the binding of >> the reference fields. So it seems an implementation is free to hand the >> service object to a consumer before the static references are bound. This >> surprised me. >> >> For delayed components, Compendium section 112.5.4 states: "When the >> service is requested … SCR must create and activate a [unique] component >> configuration”. It does NOT explicitly state that the static references >> must be bound before the service object is handed to the caller of >> getService(). Again this surprised me. >> >> >> In summary, it’s clear to me that the *intention* of the specification is >> for static references to be bound before the service becomes accessible, >> and that component writers should not have to declare those fields >> volatile, but this intention is not adequately spelled out. The best >> statement I could find is in section 112.3.6.1: "A component instance >> never sees any of the dynamics of the static reference”, which is very >> vague. >> >> Again, I may well have missed something or made an error in my analysis. I >> look forward to be being corrected. >> >> >> Neil >> >> >> > On 2 May 2017, at 19:56, David Jencks <david.a.jen...@gmail.com> wrote: >> > >> > I don’t understand your example or I don’t understand how your example >> relates to an osgi or ds problem. >> > >> > I think you are proposing that the DS component is an http server and >> starts threads etc. If so, it’s that components responsibility to make >> sure its’ internal operations are thread safe, and this has little to do >> with osgi or ds. >> > >> > If you are asking about osgi, for instance if the component exposes >> HttpServlet and is registered with the whiteboard http service and receives >> calls from it, then the whiteboard had to call getService to get the DS >> component instance. In this case I think that there are plenty of >> synchronization barriers in the service registry and in e.g. >> SingleComponentManager.getService to assure that the view a request >> thread has of the component is the same as the view the activation thread >> had. >> > >> > I could be wrong about whether there are actually enough synchronization >> barriers. Have you looked at the code to see? >> > >> > thanks >> > david jencks >> > >> >> On May 2, 2017, at 10:45 AM, Dirk Hogan <dirk.ho...@forgerock.com> >> wrote: >> >> >> >> Hi Raymond- >> >> Assume Felix does it's thing: sets all @References, activates all >> >> components, publishes all services. It has resolved all dependencies, >> and >> >> set references accordingly. >> >> >> >> Thus, in the example above, myOSGiComponent has its @Reference to >> >> SomeOtherOSGiService resolved - Felix has set this reference. At the >> >> conclusion of the activate method, myOSGiComponent has exposed some >> >> functionality via http, even successfully dereferenced myOSGiComponent, >> and >> >> now requests start to come it, serviced by the http-server's >> thread-pool. >> >> Part of servicing this request involves dereferencing (reading) the >> >> @Reference myServiceReference, which, I agree has been set (written) by >> a >> >> Felix thread. >> >> >> >> The point of the Java Memory Model is that memory state, including >> >> references, read and written across threads, do not have to have >> >> determinate state, unless there is a Synchronization Action which >> ensures >> >> that the state written by thread A is visible to any other thread - a >> >> volatile reference provides such a Synchronization Action. So code in >> >> myOSGiComponent activate method can successfully dereference the >> >> SomeOtherOSGiService reference, because the same thread which set the >> >> reference is calling activate. But this does NOT mean that another >> thread, >> >> reading the SAME reference, will see a properly initialized reference. >> >> >> >> Again, this should be self-evident, and the various rules of the JMM >> model >> >> are defined in section 17.4 of the Java Language Specification. If it is >> >> not clear, then an understanding if the JMM is required. This >> understanding >> >> can be gained by reading the JLS, reading the links above, the chapter >> >> in *Concurrency >> >> in Practice* on the JMM, or http://gee.cs.oswego.edu/dl/cpj/jmm.html >> >> provides a good intro. >> >> >> >> The bottom line: memory state, including references, shared among >> multiple >> >> threads can have indeterminate state when written/read, unless explicit >> >> steps, defined in the JMM, are taken to ensure this visibility and thus >> >> coherent state. This is not an opinion. >> >> >> >> Thanks >> >> >> >> Dirk >> >> >> >> >> >> >> >> On Tue, May 2, 2017 at 10:23 AM, Raymond Auge <raymond.a...@liferay.com >> > >> >> wrote: >> >> >> >>> On Tue, May 2, 2017 at 1:18 PM, Raymond Auge <raymond.a...@liferay.com >> > >> >>> wrote: >> >>> >> >>>> >> >>>> >> >>>> On Tue, May 2, 2017 at 1:14 PM, Raymond Auge < >> raymond.a...@liferay.com> >> >>>> wrote: >> >>>> >> >>>>> >> >>>>> On Tue, May 2, 2017 at 11:46 AM, Dirk Hogan < >> dirk.ho...@forgerock.com> >> >>>>> wrote: >> >>>>> >> >>>>>> But no similar guarantee applies when another thread hits the >> >>>>>> component, >> >>>>>> >> >>>>> >> >>>>> I believe the discussion boils down to why you believe the above is >> >>>>> possible? >> >>>>> >> >>>>> If SCR has not yet made the component available to other threads >> (which, >> >>>>> to my knowledge, it only does by registering it as a service to the >> >>> service >> >>>>> registry) how can _another_ thread access the component at all? >> >>>>> >> >>>>> I don't even see a way for components within the same DS bundle to >> >>>>> interact with each other other than via the service registry because >> >>>>> @Reference only works through the registry. >> >>>>> >> >>>> >> >>>> Correction, components could interact with each other via the >> >>>> ComponentContext.. but I guess we'd have to know if that's part of >> your >> >>> use >> >>>> case. >> >>>> >> >>> >> >>> Actually, no, it's not even possible via the ComponentContext. It only >> >>> allows access to Services. >> >>> >> >>>> >> >>>> - Ray >> >>>> >> >>>> >> >>>>> >> >>>>> Please, can you explain in more detail what case might allow for the >> >>>>> above? Because I believe the assumption is that it's not currently >> >>>>> possible. Certainly there could be a bug and I'm certain everyone >> would >> >>>>> want to see that fixed. >> >>>>> >> >>>>> -- >> >>>>> *Raymond Augé* <http://www.liferay.com/web/raymond.auge/profile> >> >>>>> (@rotty3000) >> >>>>> Senior Software Architect *Liferay, Inc.* <http://www.liferay.com> >> >>>>> (@Liferay) >> >>>>> Board Member & EEG Co-Chair, OSGi Alliance <http://osgi.org> >> >>>>> (@OSGiAlliance) >> >>>>> >> >>>> >> >>>> >> >>>> >> >>>> -- >> >>>> *Raymond Augé* <http://www.liferay.com/web/raymond.auge/profile> >> >>>> (@rotty3000) >> >>>> Senior Software Architect *Liferay, Inc.* <http://www.liferay.com> >> >>>> (@Liferay) >> >>>> Board Member & EEG Co-Chair, OSGi Alliance <http://osgi.org> >> >>>> (@OSGiAlliance) >> >>>> >> >>> >> >>> >> >>> >> >>> -- >> >>> *Raymond Augé* <http://www.liferay.com/web/raymond.auge/profile> >> >>> (@rotty3000) >> >>> Senior Software Architect *Liferay, Inc.* <http://www.liferay.com> >> >>> (@Liferay) >> >>> Board Member & EEG Co-Chair, OSGi Alliance <http://osgi.org> >> >>> (@OSGiAlliance) >> >>> >> > >> >>