9 Replies Latest reply on Feb 19, 2016 4:06 AM by adinn

    Shared state across rule executions

    rachmato

      Hello Byteman

       

      I have a set of rules which need to share state - one rule has access to some arbitrary state during its execution and another rule needs that state.

       

      For example, when creating a thread pool, I have access to the name associated with the thread pool, but not in later invocations of methods which make use of the thread pools. I want to be able to print out the name in the rules which do not have direct access to the name.

       

      I wrote a helper which provides a SharedMap abstraction, backed by a HashMap<Object, Object> map, with get and put operations. This allows me to share arbitrary state across rule invocations, but setting up helpers is an extra step which i'd like to avoid.

       

      Is there a "built-in" way to share arbitrary state across rule executions in Byteman?

       

      Richard

        • 1. Re: Shared state across rule executions
          adinn

          Richard Achmatowicz wrote:

           

          Hello Byteman

           

          Hello Richard :-)

           

          I have a set of rules which need to share state - one rule has access to some arbitrary state during its execution and another rule needs that state.
          

           

          For example, when creating a thread pool, I have access to the name associated with the thread pool, but not in later invocations of methods which make use of the thread pools. I want to be able to print out the name in the rules which do not have direct access to the name.

           

          I wrote a helper which provides a SharedMap abstraction, backed by a HashMap<Object, Object> map, with get and put operations. This allows me to share arbitrary state across rule invocations, but setting up helpers is an extra step which i'd like to avoid.

           

          Is there a "built-in" way to share arbitrary state across rule executions in Byteman?

           

           

          Unfortunately, this use case is not currently supported by the standard helper class. A solution using a map like you describe is something I have been wanting to implement as a built-in helper method for some time. So, I will be happy to add this as a new feature. It's important that the operations are atomic and thread-safe and also that the 'put' method returns any existing reference. Rather than put and get I'd prefer to use the following names:

           

          /**
          * atomically link name to value returning any previously
          * linked Object or null if no link is in place
          */
          Object link(Object name, Object value) { . . . }
          /*
          * retrieve the Object name is currently linked to or null
          * if no link is in place
          */
          Object linked(Object name) { . . . }
          /*
          * atomically remove any link from name returning the
          * Object it is currently linked to or null if no link is
          * in place
          */
          Object unlink(Object name) { return link(name, null); }
          
          

           

          Could you either send me a patch or -- better -- clone the master repo, add your code to Helper and then post a Pull Request for the commit.

          • 2. Re: Shared state across rule executions
            rachmato

            Hi Andrew

             

            Will do. Do you have any preference for the name of the construct, or is SharedMap OK? How about just plain Map, as in Counter and Rendezvous?

            • 3. Re: Shared state across rule executions
              adinn

              I don't mind that much what you do under the API but I don't think you need an auxiliary class as for CountDown etc.

               

              You probably just need a static field in Helper of type HashMap<Object, Object> as per countDownMap or rendezvousMap. Maybe call it linkMap to be consistent with the built-in names? i.e.

               

              /**
              * a hash map used to store links from some name object to
              * an associated value object
              */
              private static HashMap<Object, Object> linkMap = new HashMap<Object, Object>();
              
              

               

              The built-ins will need to synchronize on this object in order to modify links atomically.

              • 4. Re: Shared state across rule executions
                adinn

                Oh, wait a minute, I think I might have got the wrong end of the stick here. I was assuming you were talking about one single global map. Are you proposing having multiple such maps? That actually sounds like a very good idea.

                 

                If so then I think you need a type called LinkMap and the API needs to be richer

                 

                /*
                * create a LinkMap used to store links between names and values
                */
                boolean createLinkMap(Object mapName) { ... }
                /*
                * delete a LinkMap used to store links between names and values
                */
                boolean deleteLinkMap(Object mapName) { ... }
                
                /**
                * atomically add a link from name to value to the LinkMap
                * identified by mapName eturning any previously linked
                * Object or null if no link currently exists in the map
                */
                Object link(Object mapName, Object name, Object value) { ... }
                /*
                * retrieve the Object name is currently linked to from
                * the LinkMap named by mapName or null* if no link currently
                * exists in the map
                */
                Object linked(Object mapName, Object name) { ... }
                /*
                * atomically remove any link from name returning the
                * Object it is currently linked to or null if no link
                * currently exists in the map
                */
                Object unlink(Object mapName, Object name) { return link(mapname, name, null); }
                
                
                
                

                 

                Is this what you were thinking of?

                 

                LinkMaps could be created on demand as well as explicitly created by createLinkMap.

                 

                Also, we could have a default linkMap identified by mapName "default" which could be accessed by these variants

                 

                /**
                * add a link to the default LinkMap by calling link("default", name, value)
                */
                Object link(Object name, Object value) { return link("default", name, value); }
                /**
                * retrieve a link from the default LinkMap by calling linked("default", name)
                */
                Object linked(Object name) { return link("default", name); }
                /**
                * delete a link from the default LinkMap by calling unlink("default", name)
                */
                Object linked(Object name) { return link("default", name); }
                
                • 5. Re: Shared state across rule executions
                  rachmato

                  Yes, that's what I did yesterday. One map per object instance. The original idea would allow the user to create their own map and link it, but this would be a little more convenient IMHO. I'll set up the fix in the next hours - other stuff to do.

                  • 6. Re: Shared state across rule executions
                    adinn

                    Ok, that's great. Thanks for coming up with this excellent idea.

                    • 7. Re: Shared state across rule executions
                      adinn

                      Created BYTEMAN-313

                      • 8. Re: Shared state across rule executions
                        jameslivingston

                        There has been a few time where I want to store per-thread information, for example saving something in an acquisition method and using it in the release method. The object+key version of this would cover that, by using Thread.currentThread() as the object, so I think it will be very useful.

                        • 9. Re: Shared state across rule executions
                          adinn

                          James Livingston wrote:

                           

                          There has been a few time where I want to store per-thread information, for example saving something in an acquisition method and using it in the release method. The object+key version of this would cover that, by using Thread.currentThread() as the object, so I think it will be very useful.

                           

                          Yes, that's the basic case that the default LinkMap supports.

                           

                          However, when you need to save more than one piece of data per thread then you could use the thread to label a LinkMap and then link each piece of data using an identifying key such as a global String e.g.

                           

                          . . .
                          BIND th = Thread.currentThread();
                          . . .
                          DO link(th, "wait-time", getElapsedTime(th);
                             link(th, "result", $!);
                          . . .
                          

                           

                          Equivalently, you could also label the maps using the constants "wait-time" and "result" and then link the thread to the value in each of these maps.

                           

                          . . .
                          BIND th = Thread.currentThread()
                          . . .
                          DO link("wait-time", th, getElapsedTime(th));
                             link("result", th, $!);
                          . . .
                          

                           

                          The second approach has the advantage that it requires less LinkMaps to be created but is otherwise functionally much the same given the current LinkMap builtin API. The choice will become more significant when we add extra builtins to aggregate data from a LinkMap into a collection:

                           

                          List<Object> linkKeys(Object mapName);
                          List<Object> linkValues(Object mapName);
                          List<Pair<Object, Object>> linkPairs(Object mapName);
                          

                           

                          Clearly, the way you order the map name and key makes a big difference to how you can employ these builtins.

                           

                          Strictly these new builtins ought to return a Set rather than a List since the keys/values being stuffed into a List cannot actually be assumed to be ordered. However, I chose List because I'd prefer to avoid adding another JDK class into Byteman's footprint. Anyway, it is good to convert the Set instances returned by the HashMap API to a List since the former are are backed by the HashMap so cannto be relied upon to be thread-safe. I think we have to provide an implementation for Pair in Byteman itself as there is no convenient implementation we can use in all JDKs from 6 up.