Storing data using chunks
By themselves plug-ins can only store global state which must be protected by threading primitives from access from multiple EPL contexts simultaneously. It is possible for plug-ins to store state in an opaque (to EPL) object. This can be stored specific to a single monitor instance and then passed back into the plug-in for further processing. In EPL, this is represented using a chunk object. Chunk objects are specific to a single plug-in, and an attempt to pass them to other plug-ins will throw an exception.
You can store any C++ type from your plug-in in a chunk, provided that it is copyable using the default copy constructor. If you have multiple different types from a single plug-in stored in EPL chunks, then you must have them share a class hierarchy so that you can distinguish them when they are passed back from EPL.
The argument and return type that you must use to pass chunks is custom_t<T>. custom_t is templated over the type that you are storing in the chunk. Chunks can only be created by your plug-in and returned from a function to EPL. When they are passed back to your plug-in, you can modify the referred to class, but not change which object it points to. Chunk objects are garbage collected by the EPL runtime for you and will be deleted when they go out of scope. You do not retain ownership of them.
If you need lifetime shared between EPL and the plug-in (or another chunk object), then indirectly access the shared object via std::shared_ptr. The custom_t would then point to a std::shared_ptr or a class containing a std::shared_ptr, and the plug-in follows the shared_ptr to access the shared object. The plug-in can also have a std::shared_ptr to the shared object. The object will not be destroyed until both EPL has garbage collected the chunk object and the plug-in has destroyed its shared_ptr to the object.
class EPLData
{
public:
EPLData(int64_t value): value(value) {}
// default copy constructor and destructor
int64_t value;
};
custom_t<EPLData> createChunk(int64_t value)
{
return custom_t<EPLData>(new EPLData(value));
}
void logChunkValue(const custom_t<EPLData> &chunk)
{
logger.info("Chunk value is: %" PRId64, chunk->value);
}
monitor m {
import "DataPlugin" as plugin;
action onload() {
chunk c := plugin.createChunk(42);
plugin.logChunkValue(c);
}
}
Chunk objects can be copied in EPL. This occurs when the explicit clone() method is called on a chunk or an event containing the chunk, or if the monitor spawns. When a chunk object is copied, the value in the custom_t is copied, using the default copy constructor of the template parameter for custom_t. Thus, each instance will only be accessible from a single monitor instance and thus only from one thread at a time (though copies may be accessed concurrently to the original object).
If you have multiple different chunk types, then use the base class as the template parameter for custom_t and then use dynamic_cast or virtual dispatch to distinguish between them. When returning a custom_t value, you must use the derived type as the template parameter, because that is the type used to create a copy of the object, if needed.
class ChunkBase
{
virtual void doSomething() = 0;
};
class DerivedChunk: public ChunkBase
{
};
void modifyChunk(const custom_t<ChunkBase> &chunk)
{
chunk->doSomething(); // virtual dispatch
auto derived = dynamic_cast<DerivedChunk*>(chunk.get());
if (derived) {
// derived-specific methods
}
};