Skip to content

SignalSlotable

class SignalSlotable : public std::enable_shared_from_this<SignalSlotable>

The SignalSlotable class. This class implements the so-called "Signal-Slot" design pattern orginally termed by the Qt-Gui framework. However, signals and slots are not restricted to a local application but can be connected and triggered across the network. This allows for programming with network components in the same intuitive (event-driven) way as Qt allows to do with its local components (e.g. widgets).

Moreover does this implementation (unlike Qt) not require any pre-processing. Another additional feature is the ability to setup new signals and/or slots at runtime.

Furthermore, this class implements functions for the common request/response patterns.

For a full documentation of the signal-slot component see the documentation in the software-guide.

Types

Name Description
SignalSlotConnection A structure to keep information of a signal-slot connection
AsyncReply A functor to place an asynchronous reply during slot execution.
Requestor

Type Aliases

Name Description
AsyncErrorHandler An AsyncErrorHandler takes no argument, but it will be called such that it can rethrow and then catch exceptions.

Functions

Name Description
SignalSlotable This constructor does nothing.
SignalSlotable Creates a functional SignalSlotable object using an existing connection.
SignalSlotable Creates a function SignalSlotable object allowing to configure the broker connection.
init Initializes the SignalSlotable object (only use in conjunction with empty constructor).
start This function starts the communication.
eraseTrackedInstance Erase instance from container of tracked instances To be called if one is tracking instances and is sure that the given instance is not alive anymore (e.g. if another instance in the same process is dead as well). If erroneously called, the next arriving heartbeat of the instance will trigger an instanceNew event instanceId : that shall be treated as not alive anymore Return : whether instanceId was tracked before
getAvailableSignals This is a synchronous call with timeout in milliseconds return vector of device signals.
getAvailableSlots This is a synchronous call with timeout in milliseconds return vector of device slots.
getUserName Retrieves currently logged in username (empty if not logged in) Return : string username
getInstanceId Access to the identification of the current instance using signals and slots Return : instanceId
updateInstanceInfo Update and publish the instanceInfo update: : a Hash containing new or updated keys - or keys to remove remove: : if false (default), merge 'update' to existing instance info, otherwise subtract it
getSenderInfo This function must only be called within a slotFunctions body.
connect This function tries to connect a remote signal to one of our slots.
asyncConnect This function tries to establish asynchronously a connection between a signal and a slot, identified both by their respective instance IDs and signatures.
asyncConnect This function tries to establish asynchronously a connection between several signals and slots.
disconnect Disconnects a slot from a signal.
asyncDisconnect This function tries to disconnect a previously established connection between a signal and a slot.
emit Emits a signal, i.e. publishes the given payload Emitting a signal is a fire-and-forget activity. The function returns immediately. signalFunction : The name of the previously registered signal args... : A variadic number of arguments to be published
call Calls a (remote) function.
reply Place the reply of a slot call To be used inside a method registered as a slot.
registerSlot Register a new slot function for a slot.
createInputChannel Create and register an InputChannel together with handlers channelName : name of the channel, e.g. its path in the schema config : is a Hash with a Hash at 'channelName' which will be passed to InputChannel::create onDataAvailableHandler : is a DataHandler called for each data item coming through the pipeline onInputAvailableHandler : is an InputHandler called when new data arrives - user has to loop over all items onEndOfStreamEventHandler : is an InputHandler called when EOS is received connectTracker : will be called whenever the connection status of the created channel changes Return : the created InputChannel - better do not store it anywhere, it can be received via getInputChannel(channelName)
removeInputChannel Remove the InputChannel created via createInputChannel channelName : identifies the channel (first argument that was given to createInputChannel) Return : true if such a channel existed and could be removed
createOutputChannel Create an OutputChannel under the given name If there is already one for than name, that one (and thus all other copies of its shared_ptr) will be disabled to disconnect any connection.
removeOutputChannel Remove the OutputChannel created via createOutputChannel Before removal, it (and thus all other copies of its shared_ptr) will be disabled to disconnect any connection.
getOutputChannel Access pointer to OutputChannel with given name.
getOutputChannelNoThrow Access pointer to OutputChannel with given name.
getInputChannel Access pointer to InputChannel with given name.
getInputChannelNoThrow Access pointer to InputChannel with given name.
connectInputChannel Deprecated, use asyncConnectInputChannel!
asyncConnectInputChannel Connect input channel to output channels defined in its configuration.
connectInputChannels Trigger connection of all not connected input channel connections Re-triggers itself regularly via internal m_channelConnectTimer e : do nothing if evaluates to true
fetchInstanceId Parses out the instanceId part of signalId or slotId signalOrSlotId : Return : A string representing the instanceId
registerBroadcastHandler Register a handler to be called for every received message that is addressed to everybody.
addSignalIfNew Helper for registerSignal: If signalFunction is not yet known, creates a signal corresponding to the template argument signature and adds it to the internal container. Otherwise an empty pointer is returned. signalFunction : Return : pointer to new Signal or empty pointer
ensureInstanceIdIsValid If instanceId has invalid characters, throws SignalSlotException.
ensureInstanceIdIsUnique If instanceId not unique in system, throws SignalSlotException.
registerAsyncReply Internal method to provide info for AsyncReply object Return : tuple of slot header, slot name and whether it is a global slot call
reconnectSignals Calls connect for all signal-slot connections that involve 'newInstanceId' (be it on signal or slot side) and for which this instance is responsible, i.e. its "connect" has been called for this connection before (with or without immediate success). Calling "disconnect" stops this responsibility.
registerForShortcutMessaging Register myself for short-cut messaging (i.e. bypass broker if in same process). Must not be called before instance ID is checked to be unique in overall system.
deregisterFromShortcutMessaging Deregister myself from short-cut messaging.
registerNewSlot Register a new slot instance under name funcName.
slotConnectToSignal Register signal-slot connection on signal side
slotSubscribeRemoteSignal Slot to subscribe to remote signal
slotUnsubscribeRemoteSignal Slot to un-subscribe from remote signal
instanceHasSlot True if instance with ID 'slotInstanceId' has slot 'slotFunction'.
slotHasSlot Slot to tell whether instance has a slot of given name.
handleInputConnected helper for connectInputChannels()
tryToUnregisterSlot Try to undo registration of a slot "slotInstanceId.slotFunction". Thread-safe, locks m_signalSlotInstancesMutex. signalFunction : name of local signal slotInstanceId : instance id that carries the slot slotFunction : the slot - if empty, all registered slots of slotInstanceId Return : bool true if signal existed and given slot was registered before
addReceiveAsyncErrorHandles For the given replyId of a 'request.receiveAsync', register error handling, i.e. the timer for timeout and the handler for remote exceptions.
callErrorHandler Helper that calls 'handler' such that it can do try { throw; } catch (const SignalSlotException &e) { } message : text given to the SignalSlotException

Type Alias Details

AsyncErrorHandler

typedef std::function<void()> AsyncErrorHandler

An AsyncErrorHandler takes no argument, but it will be called such that it can rethrow and then catch
exceptions. The caught exception indicates the failure reason, e.g.:

void asyncErrorHandler () { try { throw; } catch (std::exception& e) { // or any other exception type - or several catch statements KARABO_LOG_FRAMEWORK_WARN << "Probem when trying to do something: " << e.what(); } }

Function Details

SignalSlotable

SignalSlotable()

This constructor does nothing. Call init() afterwards for setting up.

SignalSlotable(const std::string& instanceId, const karabo::net::Broker::Pointer& connection, const int heartbeatInterval = 30, const karabo::data::Hash& instanceInfo = karabo::data::Hash())

Creates a functional SignalSlotable object using an existing connection.

Don't call init() afterwards.

instanceId : The future instanceId of this object in the distributed system

connection : An existing broker connection

heartbeatInterval : The interval (in s) in which a heartbeat is emitted

instanceInfo : A hash containing any important additional information

explicit SignalSlotable(const std::string& instanceId, const karabo::data::Hash& brokerConfiguration = karabo::data::Hash(), const int heartbeatInterval = 30, const karabo::data::Hash& instanceInfo = karabo::data::Hash())

Creates a function SignalSlotable object allowing to configure the broker connection.

Don't call init() afterwards.

instanceId : The future instanceId of this object in the distributed system

brokerConfiguration : A single keyed Hash where the key is the broker type and the Hash at that key is the configuration for the respective broker type (a given instanceId it will be replaced by the first constructor argument). Can be empty or can contain an empty Hash() at the single key, i.e. will be expanded from defaults.

heartbeatInterval : The interval (in s) in which a heartbeat is emitted

instanceInfo : A hash containing any important additional information

addReceiveAsyncErrorHandles

void addReceiveAsyncErrorHandles(const std::string& replyId, const std::shared_ptr<boost::asio::steady_timer>& timer, const AsyncErrorHandler& errorHandler)

For the given replyId of a 'request.receiveAsync', register error handling,
i.e. the timer for timeout and the handler for remote exceptions.

addSignalIfNew

template <typename... Args> SignalInstancePointer addSignalIfNew(const std::string& signalFunction)

Helper for registerSignal: If signalFunction is not yet known, creates a signal corresponding to the template argument signature and adds it to the internal container. Otherwise an empty pointer is returned.

signalFunction :

Return : pointer to new Signal or empty pointer

asyncConnect

void asyncConnect(const std::string& signalInstanceId, const std::string& signalSignature, const std::string& slotInstanceId, const std::string& slotSignature, const std::function<void()>& successHandler = std::function<void()>(), const AsyncErrorHandler& failureHandler = AsyncErrorHandler(), int timeout = 0)

This function tries to establish asynchronously a connection between a signal and a slot, identified both by their respective instance IDs and signatures. Moreover, this SignalSlotable obeys (throughout its full lifetime or until "disconnect" is called with the same arguments) the responsibility to keep this connection alive, i.e. to reconnect if either signal or slot instance come back after they have shutdown or if they come up the first time.

signalInstanceId : is the instance ID of the signal (if empty use this instance)

signalSignature : is the signature of the signal

slotInstanceId : is the instance ID of the slot (if empty use this instance)

slotSignature : is the signature of the slot

successHandler : is called when connection is established (maybe be empty [=default])

failureHandler : is called when connection could not be established, in the same way as an Requestor::AsyncErrorHandler - if Signal or Slot do not exist, the exception is a SignalSlotException

timeout : in milliseconds for internal async requests - non-positive (default) means the very long default timeout

void asyncConnect(const std::vector<SignalSlotConnection>& signalSlotConnections, const std::function<void()>& successHandler = std::function<void()>(), const AsyncErrorHandler& failureHandler = AsyncErrorHandler(), int timeout = 0)

This function tries to establish asynchronously a connection between several signals and slots.

One of the two handlers will be called exactly once. The failureHandler will be called if any signal slot connection failed, no matter whether other connections succeeded or not.

signalSlotConnections : e.g. vector{SignalSlotConnection("sigInst", "signal", "slotInst", "slot"), ...}

successHandler : is called when all connections are established (maybe be empty [=default])

failureHandler : is called when any of the connections could not be established, no matter whether the others failed or not, in the same way as a Requestor::AsyncErrorHandler.

timeout : in milliseconds for internal async requests - non-positive (default) means the very long default timeout

asyncConnectInputChannel

void asyncConnectInputChannel( const InputChannel::Pointer& channel, const std::function<void(bool)>& handler, const std::vector<std::string>& outputChannelsToIgnore = std::vector<std::string>())

Connect input channel to output channels defined in its configuration.

Proper asynchronous implementation with feedback handler

channel : pointer to InputChannel

handler : to report success or failure. In the latter case the argument is false and more information about the failure can be retrieved via try { throw; } catch (const std::exception&e) { const std::string reason(e.what());} in the same way as in SignalSlotable::AsyncErrorHandler

outputChannelsToIgnore : outputChannels that shall not be connected, e.g. because they are already connected (defaults to empty vector)

asyncDisconnect

void asyncDisconnect(const std::string& signalInstanceId, const std::string& signalFunction, const std::string& slotInstanceId, const std::string& slotFunction, const std::function<void()>& successHandler = std::function<void()>(), const AsyncErrorHandler& failureHandler = AsyncErrorHandler(), int timeout = 0)

This function tries to disconnect a previously established connection between a signal and a slot. These two are identified both by their respective instance IDs and signatures. In case the connection was established by this instance, the function also erases it from the list of connections that have to be re-established in case signal or slot instances come back after a shutdown.

signalInstanceId : is the instance ID of the signal (if empty use this instance)

signalSignature : is the signature of the signal

slotInstanceId : is the instance ID of the slot (if empty use this instance)

slotSignature : is the signature of the slot

successHandler : is called when connection is successfully stopped (maybe be empty [=default])

failureHandler : is called when the disconnection failed (maybe be empty [=default])

timeout : in milliseconds for internal async requests - non-positive (default) means the very long default timeout

call

template <typename... Args> void call(const std::string& instanceId, const std::string& functionName, const Args&... args) const

Calls a (remote) function. Calling a remote function is a fire-and-forget activity. The function returns immediately after sending the message.

instanceId : Instance to be called

functionName : Function on instance to be called (must be a registered slot)

args : Arguments with which to call the slot

callErrorHandler

static void callErrorHandler(const AsyncErrorHandler& handler, const std::string& message)

Helper that calls 'handler' such that it can do

try { throw; } catch (const SignalSlotException &e) { }

message : text given to the SignalSlotException

connect

bool connect(const std::string& signalInstanceId, const std::string& signalSignature, const std::string& slotSignature)

This function tries to connect a remote signal to one of our slots.

Moreover, this SignalSlotable obeys (throughout its full lifetime or until "disconnect" is called with the same arguments) the responsibility to keep this connection alive, i.e. to reconnect if either signal or slot instance come back after they have shutdown or if they come up the first time.

signalInstanceId : is the instance ID of the signal (if empty use this instance)

signalSignature : is the signature of the signal

slotSignature : is the signature of the slot

Return : whether connection is already successfully established

connectInputChannel

void connectInputChannel(const InputChannel::Pointer& channel, int trails = 8)

Deprecated, use asyncConnectInputChannel!

Connects an input channel to those as defined on the input channel's configuration. The function is asynchronous, but gives no feedback about success or failure.

connectInputChannels

void connectInputChannels(const boost::system::error_code& e)

Trigger connection of all not connected input channel connections

Re-triggers itself regularly via internal m_channelConnectTimer

e : do nothing if evaluates to true

createInputChannel

virtual InputChannel::Pointer createInputChannel( const std::string& channelName, const karabo::data::Hash& config, const DataHandler& onDataAvailableHandler = DataHandler(), const InputHandler& onInputAvailableHandler = InputHandler(), const InputHandler& onEndOfStreamEventHandler = InputHandler(), const InputChannel::ConnectionTracker& connectTracker = InputChannel::ConnectionTracker())

Create and register an InputChannel together with handlers

channelName : name of the channel, e.g. its path in the schema

config : is a Hash with a Hash at 'channelName' which will be passed to InputChannel::create

onDataAvailableHandler : is a DataHandler called for each data item coming through the pipeline

onInputAvailableHandler : is an InputHandler called when new data arrives - user has to loop over all items

onEndOfStreamEventHandler : is an InputHandler called when EOS is received

connectTracker : will be called whenever the connection status of the created channel changes

Return : the created InputChannel - better do not store it anywhere, it can be received via getInputChannel(channelName)

createOutputChannel

virtual OutputChannel::Pointer createOutputChannel( const std::string& channelName, const karabo::data::Hash& config, const OutputHandler& onOutputPossibleHandler = OutputHandler())

Create an OutputChannel under the given name

If there is already one for than name, that one (and thus all other copies of its shared_ptr) will be disabled to disconnect any connection.

channelName : the name for the channel

config : must have a Hash at key channelName - that is passed (after removeal of the "schema" key) to Configurator::create

onOutputPossibleHandler : ?

Return : pointer to created channel - do not store anywhere! If needed, retrieve again via getOutputChannel(channelName).

deregisterFromShortcutMessaging

void deregisterFromShortcutMessaging()

Deregister myself from short-cut messaging.

disconnect

bool disconnect(const std::string& signalInstanceId, const std::string& signalFunction, const std::string& slotFunction)

Disconnects a slot from a signal.

Also erase it from the list of connections that have to re-established in case signal instance comes back after a shutdown.

signalInstanceId : is the instance ID of the signal (if empty use this instance)

signalSignature : is the signature of the signal

slotSignature : is the signature of the slot

Return : whether connection is successfully stopped, e.g. false if there was no such connection or if remote signal instance ID did not confirm in time

emit

template <typename... Args> void emit(const std::string& signalFunction, const Args&... args) const

Emits a signal, i.e. publishes the given payload Emitting a signal is a fire-and-forget activity. The function returns immediately.

signalFunction : The name of the previously registered signal

args... : A variadic number of arguments to be published

ensureInstanceIdIsUnique

void ensureInstanceIdIsUnique(const std::string& instanceId)

If instanceId not unique in system, throws SignalSlotException.

ensureInstanceIdIsValid

void ensureInstanceIdIsValid(const std::string& instanceId)

If instanceId has invalid characters, throws SignalSlotException.

eraseTrackedInstance

bool eraseTrackedInstance(const std::string& instanceId)

Erase instance from container of tracked instances

To be called if one is tracking instances and is sure that the given instance is not alive anymore (e.g. if another instance in the same process is dead as well). If erroneously called, the next arriving heartbeat of the instance will trigger an instanceNew event

instanceId : that shall be treated as not alive anymore

Return : whether instanceId was tracked before

fetchInstanceId

std::string fetchInstanceId(const std::string& signalOrSlotId) const

Parses out the instanceId part of signalId or slotId

signalOrSlotId :

Return : A string representing the instanceId

getAvailableSignals

std::vector<std::string> getAvailableSignals(const std::string& instanceId, int timeout = 100)

This is a synchronous call with timeout in milliseconds return vector of device signals.

instanceId : of the device

timeout : in milliseconds

Return : vector of device's signal names

getAvailableSlots

std::vector<std::string> getAvailableSlots(const std::string& instanceId, int timeout = 100)

This is a synchronous call with timeout in milliseconds return vector of device slots.

instanceId : of the device

timeout : in milliseconds

Return : vector of device's slot names

getInputChannel

InputChannel::Pointer getInputChannel(const std::string& name)

Access pointer to InputChannel with given name. Throws ParameterException if no such input channel.

Return : InputChannel::Pointer

getInputChannelNoThrow

InputChannel::Pointer getInputChannelNoThrow(const std::string& name)

Access pointer to InputChannel with given name.

name : of input channel (e.g. path in expectedParameters)

Return : InputChannel::Pointer - empty if no channel of that name

getInstanceId

const std::string& getInstanceId() const

Access to the identification of the current instance using signals and slots

Return : instanceId

getOutputChannel

OutputChannel::Pointer getOutputChannel(const std::string& name)

Access pointer to OutputChannel with given name. Throws ParameterException if no such output channel.

name : of output channel (e.g. path in expectedParameters)

Return : OutpuChannel::Pointer

getOutputChannelNoThrow

OutputChannel::Pointer getOutputChannelNoThrow(const std::string& name)

Access pointer to OutputChannel with given name.

name : of output channel (e.g. path in expectedParameters)

Return : OutputChannel::Pointer - empty if no channel of that name

getSenderInfo

const SlotInstancePointer& getSenderInfo(const std::string& slotFunction)

This function must only be called within a slotFunctions body. It returns the current object handling the callback which provides more information on the sender.

slotFunction : The string-ified name of the slotFunction you are currently in

Return : instance of a Slot object (handler object for this callback)

getUserName

const std::string& getUserName() const

Retrieves currently logged in username (empty if not logged in)

Return : string username

handleInputConnected

void handleInputConnected(bool success, const std::string& channel, const std::shared_ptr<std::mutex>& mut, const std::shared_ptr<std::vector<karabo::net::AsyncStatus>>& status, size_t i, size_t numOutputsToIgnore)

helper for connectInputChannels()

init

void init(const std::string& instanceId, const karabo::net::Broker::Pointer& connection, const int heartbeatInterval, const karabo::data::Hash& instanceInfo, bool consumeBroadcasts = true)

Initializes the SignalSlotable object (only use in conjunction with empty constructor).

instanceId : The future instanceId of this object in the distributed system

connection : An existing broker connection

heartbeatInterval : The interval (in s) in which a heartbeat is emitted

instanceInfo : A hash containing any important additional information

consumeBroadcasts : if true (default), receive messages addressed to everybody (i.e. to '*') on its own. If false, some other mechanism has to ensure to deliver these.

instanceHasSlot

bool instanceHasSlot(const std::string& slotInstanceId, const std::string& unmangledSlotFunction)

True if instance with ID 'slotInstanceId' has slot 'slotFunction'.
Internally uses "slotHasSlot" for remote instances, but shortcuts if ID is the own one.
Always true if 'slotInstanceId == "*"' (i.e. global slot).

reconnectSignals

void reconnectSignals(const std::string& newInstanceId)

Calls connect for all signal-slot connections that involve
'newInstanceId' (be it on signal or slot side) and for which
this instance is responsible, i.e. its "connect" has been called
for this connection before (with or without immediate success).
Calling "disconnect" stops this responsibility.

registerAsyncReply

std::tuple<karabo::data::Hash::Pointer, std::string, bool> registerAsyncReply()

Internal method to provide info for AsyncReply object

Return : tuple of slot header, slot name and whether it is a global slot call

registerBroadcastHandler

void registerBroadcastHandler(std::function<void(const karabo::data::Hash::Pointer& header, const karabo::data::Hash::Pointer& body)> handler)

Register a handler to be called for every received message that is addressed to everybody. NOTE: This is not thread safe - call before SignalSlotable::start starts receiving messages.

handler : with header and body (as Hash::Pointer) of the message

registerForShortcutMessaging

void registerForShortcutMessaging()

Register myself for short-cut messaging (i.e. bypass broker if in same process).
Must not be called before instance ID is checked to be unique in overall system.

registerNewSlot

void registerNewSlot(const std::string& funcName, SlotInstancePointer instance)

Register a new slot instance under name funcName. This will raise an error if the slot already exists.

registerSlot

void registerSlot(const std::function<void()>& slot, const std::string& funcName)

Register a new slot function for a slot. A new slot is generated if so necessary. It is checked that the signature of the new slot is the same as an already registered one.

removeInputChannel

virtual bool removeInputChannel(const std::string& channelName)

Remove the InputChannel created via createInputChannel

channelName : identifies the channel (first argument that was given to createInputChannel)

Return : true if such a channel existed and could be removed

removeOutputChannel

virtual bool removeOutputChannel(const std::string& channelName)

Remove the OutputChannel created via createOutputChannel

Before removal, it (and thus all other copies of its shared_ptr) will be disabled to disconnect any connection.

channelName : identifies the channel (first argument that was given to createOutputChannel)

Return : true if such a channel existed and could be removed

reply

template <typename... Args> void reply(const Args&... args)

Place the reply of a slot call

To be used inside a method registered as a slot. The reply is not directly sent, but it is registered to be sent once all methods registered to the slot (usually only one) have finished execution. So if called several times in a slot, the last call defines the actual reply.

If this method is not called inside a slot, an "empty" reply will be send without arguments. But note that Device::updateState(const State s, ...) implicitly calls reply(s.name()).

See about AsyncReply to avoid blocking the thread in case reply values are known only later, e.g. after some IO operations.

args : 0 to 4 objects of the types known to serialisation, e.g. float, vector, Hash,...

slotConnectToSignal

void slotConnectToSignal(const std::string& signalFunction, const std::string& slotInstanceId, const std::string& slotFunction)

Register signal-slot connection on signal side

slotHasSlot

void slotHasSlot(const std::string& unmangledSlotFunction)

Slot to tell whether instance has a slot of given name.

slotSubscribeRemoteSignal

void slotSubscribeRemoteSignal(const std::string& signalInstanceId, const std::string& signalFunction)

Slot to subscribe to remote signal

slotUnsubscribeRemoteSignal

void slotUnsubscribeRemoteSignal(const std::string& signalInstanceId, const std::string& signalFunction)

Slot to un-subscribe from remote signal

start

void start()

This function starts the communication.

After a call to this non-blocking function the object starts listening to messages. The uniqueness of the instanceId is validated (throws SignalSlotException if not unique) and if successful the object registers with a call to "slotInstanceNew" to the distributed system.

tryToUnregisterSlot

bool tryToUnregisterSlot(const std::string& signalFunction, const std::string& slotInstanceId, const std::string& slotFunction)

Try to undo registration of a slot "slotInstanceId.slotFunction".
Thread-safe, locks m_signalSlotInstancesMutex.

signalFunction : name of local signal

slotInstanceId : instance id that carries the slot

slotFunction : the slot - if empty, all registered slots of slotInstanceId

Return : bool true if signal existed and given slot was registered before

updateInstanceInfo

void updateInstanceInfo(const karabo::data::Hash& update, bool remove = false)

Update and publish the instanceInfo

update: : a Hash containing new or updated keys - or keys to remove

remove: : if false (default), merge 'update' to existing instance info, otherwise subtract it