Skip to content

Releases: aleph2c/miros

Thread-safe live instrumentation, print and signal catching glob support

02 Jan 14:12
Compare
Choose a tag to compare

Added four new internal signals: STOP_FABRIC_SIGNAL, STOP_ACTIVE_OBJECT_SIGNAL, SUBSCRIBE_META_SIGNAL, PUBLISH_META_SIGNAL. These signals used to be created by the active object, but they have been moved into the event.py class, so that signal catching globs can be supported by miros.

Writes to Python print are not thread safe. Before this release the default behavior of live_spy and live_trace wrote out to python's print function directly. This live instrumentation could cause an io-block and a resulting run time error. I fixed this threading issue by changing the miros active objects to write their live stream messages through a dedicated instrumentation thread. This thread ensures that only one thing gets to print at a time. Also, the active object now provides a thread safe version of print which uses the instrumentation thread.

Miros provides the user with a way to over-write how their live trace and live spy streams are written. The thread managing the live stream watches a queue, which contains 0 or more Instrumentation named tuples. An Instrumentation named tuple has a fn and a content element. The fn describes the way the content is intended to be printed, so that the register_live_spy_callback and register_live_trace_callback features will continue to work. The user can change "how" their instrumentation stream will be represented, but the function-they-provide-to-do-this-with: fn, must be protected within a thread.

Since there can be many active objects which all want to print their live instrumentation information at the same time, but only one instrumentation thread handler, I made the class which provides the instrumentation threading a singleton. The class which provides the instrumentation threading is called InstrumentationWriterClass, and it is turned into the singleton InstrumentationWriter by the SingletonDectorator class. When an active object is being constructed it creates an InstrumentationWriter. If a previously build active object has already done this, a reference to the previously created InstrumentedWriter is returned. Then the instrumentation writing thread is started if it hasn't been started already.

Fixed task start race condition

26 Dec 21:18
Compare
Choose a tag to compare

When the start_at method is called, the statechart climbs into its initial state, then the active object starts a thread.

But, prior to this release, if the start_at method caused calls to other methods which needed a task to run, the task would be started to honor the request of the call. This meant that the task would be started too soon, and the active object could send events to the HSM dispatcher, while it was still trying to climb into its initial state. Statecharts only run one event at a time, and starting a chart while having it react to events breaks the RTC protocol meaning that the statechart could behave non-deterministically .

Specifically, the bug fixed in this release would happen when a call to subscribe, or publish, or post_fifo, or post_lifo where made during the start-up process. The start_thread_if_not_running decorator wrapped these calls so that they could work because they needed a thread to work. However it was a mistake to allow the thread to be started in more than one location in the code base. This bug has been fixed in this release and the start_thread_if_not_running decorator has been removed so that the same mistake won't be made again.

There are situations when the subscribe, or publish, or post_fifo, or post_lifo calls will be made before the thread is started. These requests must be honored but they can be deferred till after the thread is started. To grant the client access to these features without a running thread, the post_fifo and post_lifo calls put their requests directly into the Active Object's queues so that they will be acted upon when the thread is started. To do the same for the publish and subscribe calls, two new meta signals (a signal which carries another signal) were invented. If a publish method is called before a thread was started, an event with the signal named PUBLISH_META_SIGNAL is posted using the post_lifo technique into the Active Object's lifo queue. Inside of this event is enough information to publish an event with the required priority once the Active Object's thread started at a later time. A similar technique was followed to honor calls to subscribe, it's signal name is SUBSCRIBE_META_SIGNAL.

The ActiveObject's self.top state was overloaded to catch the PUBLISH_META_SIGNAL and SUBSCRIBE_META_SIGNAL and dispatch them to private functions which work once the thread is running. To improve performance the publish and subscribe methods only publish events if no thread is running. If the thread is running they call thread related private functions directly, as they did before.

In addition to this change, a subscribed method was added to the ActiveObject and ActiveFabric class. This method could be used to determine if a signal_name or event had been subscribed to for a given queue type (lifo/fifo).

Also, the stop_fabric and stop_active_object internal signals were renamed to STOP_FABRIC_SIGNAL and STOP_ACTIVE_OBJECT_SIGNAL to make their names consistent with other internal signal names.

Switching ThreadSafeAttributes code to use a threading.RLock

23 Sep 13:22
Compare
Choose a tag to compare

The previous, v4.1.3, version of miros ThreadSafeAttributes class would not protect thread-safe-attributes from race conditions when they were used with the non-atomic "+=", "-=" ... "<<=" family of operations. (I could not get the broken code to fail in my tests, it was a review that brought the error to my attention; thank you to Ljubomir Kaludjercic for pointing this out to me).

To fix this issue, the deque was removed and a threading.Rlock was used with the inspect module, within a descriptor. Upon access, the descriptor checks to see if the previous line of code contains a "+=", "-=", ... "<<=" and if so it will leave the lock active until the value is set, see the code here. The usage of this limited protection was explained and documented in the example section of miros.ca, you can see it here.

Thread Safe Attributes (attributes which can be shared across threads)

08 Sep 17:34
Compare
Choose a tag to compare

Added a ThreadSafeAttributes class which when inherited from will provide new
class level syntax for building thread safe attributes. At the class level you
would write _attributes = ['attr1', 'attr2', 'attr3'] to produce the thread
safe attributes, attr1, attr2 and attr3. (Under the hood these attributes
are actually properties of a small deque ring buffer of size 1.) To see more
about this feature reference the recipes section of the miros project at tag
v4.1.3 or later.

Allow simple methods to be registered using the "catch" method in the Factory class.

30 Jul 14:14
Compare
Choose a tag to compare

The Factory class lets a miros user build state machines within a class. Previous to this release only static methods could be registered to signals during the state construction process.

  • static methods are still permitted as catch handler inputs (backwards
    compatible)
  • standard methods are now also permitted as catch handlers too.

Allowing stop to be called within ActiveObject or Factory

15 Jul 14:07
Compare
Choose a tag to compare
  • the stop method can now be called from within an ActiveObject or a Factory.

Fixed stop method of ActiveObject

22 Jun 14:40
Compare
Choose a tag to compare
  • removed code which hides exceptions within your statechart (this might unmask
    bugs in your design)
  • changed how miros stops active objects.
    • a call to the ActiveObject stop method will not stop the active fabric
    • a call to the ActiveObject stop method will kill all slave threads which
      are posting events into that ActiveObject.
    • after the stop method is called on an ActiveObject, the ActiveFabric will
      not post events into that ActiveObject's input queue, since it won't exist
      anymore.
  • removed unnecessary files from pip package (doc/test...)