launch_daemon: Implemented sticky events, and registration.

* Sticky events are events that keep their signal raised, ie. even if
  a job is initialized afterwards, it will still be triggered.
* Consolidated naming for external events.
* Events are now registered once they are actually being used. This
  allows them to allocate the resources they need to do their thing.
This commit is contained in:
Axel Dörfler 2015-10-17 14:07:53 +02:00
parent 5ab2b1457b
commit 7cd19b7e5c
9 changed files with 178 additions and 58 deletions

View File

@ -211,8 +211,13 @@ on {
The latter form can be used to solve ambiguous event definitions. The latter form can be used to solve ambiguous event definitions.
By specifying the \c B_STICKY_EVENT flag, you can mark the event as being
a permanent change. Once triggered, such an event will stay triggered, ie.
even new targets or jobs will consider it triggered.
\param source The messenger the event is coming from. \param source The messenger the event is coming from.
\param name The name of the event. \param name The name of the event.
\param flags Flags for the event as described.
\return B_OK if the event could be registered, otherwise an error code. \return B_OK if the event could be registered, otherwise an error code.
\since Haiku R1 \since Haiku R1

View File

@ -9,6 +9,12 @@
#include <Messenger.h> #include <Messenger.h>
// Flags for RegisterEvent()
enum {
B_STICKY_EVENT = 0x01
};
class BLaunchRoster { class BLaunchRoster {
public: public:
BLaunchRoster(); BLaunchRoster();
@ -31,7 +37,7 @@ public:
status_t StartSession(const char* login); status_t StartSession(const char* login);
status_t RegisterEvent(const BMessenger& source, status_t RegisterEvent(const BMessenger& source,
const char* name); const char* name, uint32 flags);
status_t UnregisterEvent(const BMessenger& source, status_t UnregisterEvent(const BMessenger& source,
const char* name); const char* name);
status_t NotifyEvent(const BMessenger& source, status_t NotifyEvent(const BMessenger& source,
@ -44,7 +50,8 @@ private:
void _InitMessenger(); void _InitMessenger();
status_t _UpdateEvent(uint32 what, status_t _UpdateEvent(uint32 what,
const BMessenger& source, const char* name); const BMessenger& source, const char* name,
uint32 flags = 0);
private: private:
BMessenger fMessenger; BMessenger fMessenger;

View File

@ -206,9 +206,10 @@ BLaunchRoster::StartSession(const char* login)
status_t status_t
BLaunchRoster::RegisterEvent(const BMessenger& source, const char* name) BLaunchRoster::RegisterEvent(const BMessenger& source, const char* name,
uint32 flags)
{ {
return _UpdateEvent(B_REGISTER_LAUNCH_EVENT, source, name); return _UpdateEvent(B_REGISTER_LAUNCH_EVENT, source, name, flags);
} }
@ -241,7 +242,7 @@ BLaunchRoster::_InitMessenger()
status_t status_t
BLaunchRoster::_UpdateEvent(uint32 what, const BMessenger& source, BLaunchRoster::_UpdateEvent(uint32 what, const BMessenger& source,
const char* name) const char* name, uint32 flags)
{ {
if (be_app == NULL || name == NULL || name[0] == '\0') if (be_app == NULL || name == NULL || name[0] == '\0')
return B_BAD_VALUE; return B_BAD_VALUE;
@ -254,6 +255,8 @@ BLaunchRoster::_UpdateEvent(uint32 what, const BMessenger& source,
status = request.AddString("owner", be_app->Signature()); status = request.AddString("owner", be_app->Signature());
if (status == B_OK) if (status == B_OK)
status = request.AddString("name", name); status = request.AddString("name", name);
if (status == B_OK && flags != 0)
status = request.AddUInt32("flags", flags);
if (status != B_OK) if (status != B_OK)
return status; return status;

View File

@ -9,8 +9,9 @@
#include <stdio.h> #include <stdio.h>
#include <Entry.h> #include <Entry.h>
#include <ObjectList.h> #include <LaunchRoster.h>
#include <Message.h> #include <Message.h>
#include <ObjectList.h>
#include <Path.h> #include <Path.h>
#include <StringList.h> #include <StringList.h>
@ -33,8 +34,8 @@ public:
const BMessenger& Target() const; const BMessenger& Target() const;
virtual status_t Register(EventRegistrator& registrator) const; virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator) const; virtual void Unregister(EventRegistrator& registrator);
virtual void Trigger(); virtual void Trigger();
@ -48,6 +49,7 @@ protected:
BaseJob* fOwner; BaseJob* fOwner;
BMessenger fTarget; BMessenger fTarget;
BObjectList<Event> fEvents; BObjectList<Event> fEvents;
bool fRegistered;
}; };
@ -58,6 +60,8 @@ public:
OrEvent(BaseJob* owner, OrEvent(BaseJob* owner,
const BMessenger& target); const BMessenger& target);
virtual void ResetTrigger();
virtual BString ToString() const; virtual BString ToString() const;
}; };
@ -66,8 +70,8 @@ class DemandEvent : public Event {
public: public:
DemandEvent(Event* parent); DemandEvent(Event* parent);
virtual status_t Register(EventRegistrator& registrator) const; virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator) const; virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const; virtual BString ToString() const;
}; };
@ -79,16 +83,19 @@ public:
const BMessage& args); const BMessage& args);
const BString& Name() const; const BString& Name() const;
bool Resolve(); bool Resolve(uint32 flags);
virtual status_t Register(EventRegistrator& registrator) const; virtual void ResetTrigger();
virtual void Unregister(EventRegistrator& registrator) const;
virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const; virtual BString ToString() const;
private: private:
BString fName; BString fName;
BStringList fArguments; BStringList fArguments;
uint32 fFlags;
bool fResolved; bool fResolved;
}; };
@ -98,8 +105,8 @@ public:
FileCreatedEvent(Event* parent, FileCreatedEvent(Event* parent,
const BMessage& args); const BMessage& args);
virtual status_t Register(EventRegistrator& registrator) const; virtual status_t Register(EventRegistrator& registrator);
virtual void Unregister(EventRegistrator& registrator) const; virtual void Unregister(EventRegistrator& registrator);
virtual BString ToString() const; virtual BString ToString() const;
@ -198,7 +205,8 @@ EventContainer::EventContainer(Event* parent, const BMessenger* target,
const BMessage& args) const BMessage& args)
: :
Event(parent), Event(parent),
fEvents(5, true) fEvents(5, true),
fRegistered(false)
{ {
if (target != NULL) if (target != NULL)
fTarget = *target; fTarget = *target;
@ -222,7 +230,8 @@ EventContainer::EventContainer(BaseJob* owner, const BMessenger& target)
Event(NULL), Event(NULL),
fOwner(owner), fOwner(owner),
fTarget(target), fTarget(target),
fEvents(5, true) fEvents(5, true),
fRegistered(false)
{ {
} }
@ -250,8 +259,11 @@ EventContainer::Target() const
status_t status_t
EventContainer::Register(EventRegistrator& registrator) const EventContainer::Register(EventRegistrator& registrator)
{ {
if (fRegistered)
return B_OK;
int32 count = fEvents.CountItems(); int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) { for (int32 index = 0; index < count; index++) {
Event* event = fEvents.ItemAt(index); Event* event = fEvents.ItemAt(index);
@ -260,12 +272,13 @@ EventContainer::Register(EventRegistrator& registrator) const
return status; return status;
} }
fRegistered = true;
return B_OK; return B_OK;
} }
void void
EventContainer::Unregister(EventRegistrator& registrator) const EventContainer::Unregister(EventRegistrator& registrator)
{ {
int32 count = fEvents.CountItems(); int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) { for (int32 index = 0; index < count; index++) {
@ -334,6 +347,20 @@ OrEvent::OrEvent(BaseJob* owner, const BMessenger& target)
} }
void
OrEvent::ResetTrigger()
{
fTriggered = false;
int32 count = fEvents.CountItems();
for (int32 index = 0; index < count; index++) {
Event* event = fEvents.ItemAt(index);
event->ResetTrigger();
fTriggered |= event->Triggered();
}
}
BString BString
OrEvent::ToString() const OrEvent::ToString() const
{ {
@ -354,14 +381,14 @@ DemandEvent::DemandEvent(Event* parent)
status_t status_t
DemandEvent::Register(EventRegistrator& registrator) const DemandEvent::Register(EventRegistrator& registrator)
{ {
return B_OK; return B_OK;
} }
void void
DemandEvent::Unregister(EventRegistrator& registrator) const DemandEvent::Unregister(EventRegistrator& registrator)
{ {
} }
@ -381,6 +408,7 @@ ExternalEvent::ExternalEvent(Event* parent, const char* name,
: :
Event(parent), Event(parent),
fName(name), fName(name),
fFlags(0),
fResolved(false) fResolved(false)
{ {
const char* argument; const char* argument;
@ -399,26 +427,38 @@ ExternalEvent::Name() const
bool bool
ExternalEvent::Resolve() ExternalEvent::Resolve(uint32 flags)
{ {
if (fResolved) if (fResolved)
return false; return false;
fResolved = true; fResolved = true;
fFlags = flags;
return true; return true;
} }
status_t void
ExternalEvent::Register(EventRegistrator& registrator) const ExternalEvent::ResetTrigger()
{ {
return B_OK; if ((fFlags & B_STICKY_EVENT) != 0)
return;
Event::ResetTrigger();
}
status_t
ExternalEvent::Register(EventRegistrator& registrator)
{
return registrator.RegisterExternalEvent(this, Name().String(), fArguments);
} }
void void
ExternalEvent::Unregister(EventRegistrator& registrator) const ExternalEvent::Unregister(EventRegistrator& registrator)
{ {
registrator.UnregisterExternalEvent(this, Name().String());
} }
@ -441,7 +481,7 @@ FileCreatedEvent::FileCreatedEvent(Event* parent, const BMessage& args)
status_t status_t
FileCreatedEvent::Register(EventRegistrator& registrator) const FileCreatedEvent::Register(EventRegistrator& registrator)
{ {
// TODO: implement! // TODO: implement!
return B_ERROR; return B_ERROR;
@ -449,7 +489,7 @@ FileCreatedEvent::Register(EventRegistrator& registrator) const
void void
FileCreatedEvent::Unregister(EventRegistrator& registrator) const FileCreatedEvent::Unregister(EventRegistrator& registrator)
{ {
} }
@ -493,7 +533,7 @@ Events::AddOnDemand(const BMessenger& target, Event* event)
/*static*/ bool /*static*/ bool
Events::ResolveRegisteredEvent(Event* event, const char* name) Events::ResolveExternalEvent(Event* event, const char* name, uint32 flags)
{ {
if (event == NULL) if (event == NULL)
return false; return false;
@ -503,10 +543,10 @@ Events::ResolveRegisteredEvent(Event* event, const char* name)
index++) { index++) {
Event* event = container->Events().ItemAt(index); Event* event = container->Events().ItemAt(index);
if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) { if (ExternalEvent* external = dynamic_cast<ExternalEvent*>(event)) {
if (external->Name() == name && external->Resolve()) if (external->Name() == name && external->Resolve(flags))
return true; return true;
} else if (dynamic_cast<EventContainer*>(event) != NULL) { } else if (dynamic_cast<EventContainer*>(event) != NULL) {
if (ResolveRegisteredEvent(event, name)) if (ResolveExternalEvent(event, name, flags))
return true; return true;
} }
} }
@ -516,7 +556,7 @@ Events::ResolveRegisteredEvent(Event* event, const char* name)
/*static*/ void /*static*/ void
Events::TriggerRegisteredEvent(Event* event, const char* name) Events::TriggerExternalEvent(Event* event, const char* name)
{ {
if (event == NULL) if (event == NULL)
return; return;
@ -531,7 +571,7 @@ Events::TriggerRegisteredEvent(Event* event, const char* name)
return; return;
} }
} else if (dynamic_cast<EventContainer*>(event) != NULL) { } else if (dynamic_cast<EventContainer*>(event) != NULL) {
TriggerRegisteredEvent(event, name); TriggerExternalEvent(event, name);
} }
} }
} }

View File

@ -16,8 +16,11 @@ class Event;
class EventRegistrator { class EventRegistrator {
public: public:
virtual status_t RegisterEvent(Event* event) = 0; virtual status_t RegisterExternalEvent(Event* event,
virtual void UnregisterEvent(Event* event) = 0; const char* name,
const BStringList& arguments) = 0;
virtual void UnregisterExternalEvent(Event* event,
const char* name) = 0;
}; };
@ -27,13 +30,13 @@ public:
virtual ~Event(); virtual ~Event();
virtual status_t Register( virtual status_t Register(
EventRegistrator& registrator) const = 0; EventRegistrator& registrator) = 0;
virtual void Unregister( virtual void Unregister(
EventRegistrator& registrator) const = 0; EventRegistrator& registrator) = 0;
bool Triggered() const; bool Triggered() const;
virtual void Trigger(); virtual void Trigger();
void ResetTrigger(); virtual void ResetTrigger();
virtual BaseJob* Owner() const; virtual BaseJob* Owner() const;
virtual void SetOwner(BaseJob* owner); virtual void SetOwner(BaseJob* owner);
@ -42,7 +45,7 @@ public:
virtual BString ToString() const = 0; virtual BString ToString() const = 0;
private: protected:
Event* fParent; Event* fParent;
bool fTriggered; bool fTriggered;
}; };
@ -53,9 +56,9 @@ public:
static Event* FromMessage(const BMessenger& target, static Event* FromMessage(const BMessenger& target,
const BMessage& message); const BMessage& message);
static Event* AddOnDemand(const BMessenger& target, Event* event); static Event* AddOnDemand(const BMessenger& target, Event* event);
static bool ResolveRegisteredEvent(Event* event, static bool ResolveExternalEvent(Event* event,
const char* name); const char* name, uint32 flags);
static void TriggerRegisteredEvent(Event* event, static void TriggerExternalEvent(Event* event,
const char* name); const char* name);
static bool TriggerDemand(Event* event); static bool TriggerDemand(Event* event);
}; };

View File

@ -341,6 +341,14 @@ Job::IsLaunched() const
} }
bool
Job::IsRunning() const
{
// TODO: monitor team status; should jobs be allowed to run multiple times?
return State() == B_JOB_STATE_SUCCEEDED && IsLaunched() && IsService();
}
status_t status_t
Job::HandleGetLaunchData(BMessage* message) Job::HandleGetLaunchData(BMessage* message)
{ {
@ -352,10 +360,23 @@ Job::HandleGetLaunchData(BMessage* message)
} }
status_t
Job::Run()
{
status_t status = BJob::Run();
// TODO: monitor team, don't just do this
if (!IsService())
SetState(B_JOB_STATE_WAITING_TO_RUN);
return status;
}
status_t status_t
Job::Execute() Job::Execute()
{ {
if (!IsLaunched()) if (!IsLaunched() || !IsService())
return Launch(); return Launch();
return B_OK; return B_OK;

View File

@ -71,9 +71,12 @@ public:
status_t Launch(); status_t Launch();
bool IsLaunched() const; bool IsLaunched() const;
bool IsRunning() const;
status_t HandleGetLaunchData(BMessage* message); status_t HandleGetLaunchData(BMessage* message);
virtual status_t Run();
protected: protected:
virtual status_t Execute(); virtual status_t Execute();

View File

@ -85,10 +85,12 @@ class ExternalEventSource {
public: public:
ExternalEventSource(BMessenger& source, ExternalEventSource(BMessenger& source,
const char* ownerName, const char* ownerName,
const char* name); const char* name, uint32 flags);
~ExternalEventSource(); ~ExternalEventSource();
const char* Name() const; const char* Name() const;
uint32 Flags() const
{ return fFlags; }
int32 CountListeners() const; int32 CountListeners() const;
BaseJob* ListenerAt(int32 index) const; BaseJob* ListenerAt(int32 index) const;
@ -98,6 +100,7 @@ public:
private: private:
BString fName; BString fName;
uint32 fFlags;
BObjectList<BaseJob> fListeners; BObjectList<BaseJob> fListeners;
}; };
@ -108,7 +111,8 @@ typedef std::map<BString, Target*> TargetMap;
typedef std::map<BString, ExternalEventSource*> EventMap; typedef std::map<BString, ExternalEventSource*> EventMap;
class LaunchDaemon : public BServer, public Finder, public ConditionContext { class LaunchDaemon : public BServer, public Finder, public ConditionContext,
public EventRegistrator {
public: public:
LaunchDaemon(bool userMode, LaunchDaemon(bool userMode,
const EventMap& events, status_t& error); const EventMap& events, status_t& error);
@ -118,9 +122,17 @@ public:
virtual Target* FindTarget(const char* name) const; virtual Target* FindTarget(const char* name) const;
Session* FindSession(uid_t user) const; Session* FindSession(uid_t user) const;
// ConditionContext
virtual bool IsSafeMode() const; virtual bool IsSafeMode() const;
virtual bool BootVolumeIsReadOnly() const; virtual bool BootVolumeIsReadOnly() const;
// EventRegistrator
virtual status_t RegisterExternalEvent(Event* event,
const char* name,
const BStringList& arguments);
virtual void UnregisterExternalEvent(Event* event,
const char* name);
virtual void ReadyToRun(); virtual void ReadyToRun();
virtual void MessageReceived(BMessage* message); virtual void MessageReceived(BMessage* message);
@ -218,9 +230,10 @@ Session::Session(uid_t user, const BMessenger& daemon)
ExternalEventSource::ExternalEventSource(BMessenger& source, ExternalEventSource::ExternalEventSource(BMessenger& source,
const char* ownerName, const char* name) const char* ownerName, const char* name, uint32 flags)
: :
fName(name), fName(name),
fFlags(flags),
fListeners(5, true) fListeners(5, true)
{ {
} }
@ -352,6 +365,22 @@ LaunchDaemon::BootVolumeIsReadOnly() const
} }
status_t
LaunchDaemon::RegisterExternalEvent(Event* event, const char* name,
const BStringList& arguments)
{
// TODO: register actual event with event source
return B_OK;
}
void
LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name)
{
// TODO!
}
void void
LaunchDaemon::ReadyToRun() LaunchDaemon::ReadyToRun()
{ {
@ -482,7 +511,7 @@ LaunchDaemon::_HandleGetLaunchData(BMessage* message)
return; return;
} }
reply.what = B_NAME_NOT_FOUND; reply.what = B_NAME_NOT_FOUND;
} else if (!job->IsLaunched()) { } else if (job->IsService() && !job->IsLaunched()) {
if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) { if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) {
// The job exists, but cannot be started yet, as its // The job exists, but cannot be started yet, as its
// conditions are not met; don't make it available yet // conditions are not met; don't make it available yet
@ -638,6 +667,7 @@ LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
const char* name = message->GetString("name"); const char* name = message->GetString("name");
const char* ownerName = message->GetString("owner"); const char* ownerName = message->GetString("owner");
uint32 flags = message->GetUInt32("flags", 0);
BMessenger source; BMessenger source;
if (name != NULL && ownerName != NULL if (name != NULL && ownerName != NULL
&& message->FindMessenger("source", &source) == B_OK) { && message->FindMessenger("source", &source) == B_OK) {
@ -645,7 +675,7 @@ LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
ownerName = get_leaf(ownerName); ownerName = get_leaf(ownerName);
ExternalEventSource* event = new (std::nothrow) ExternalEventSource* event = new (std::nothrow)
ExternalEventSource(source, ownerName, name); ExternalEventSource(source, ownerName, name, flags);
if (event != NULL) { if (event != NULL) {
// Use short name, and fully qualified name // Use short name, and fully qualified name
BString eventName = name; BString eventName = name;
@ -723,7 +753,7 @@ LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
int32 count = event->CountListeners(); int32 count = event->CountListeners();
for (int32 index = 0; index < count; index++) { for (int32 index = 0; index < count; index++) {
BaseJob* listener = event->ListenerAt(index); BaseJob* listener = event->ListenerAt(index);
Events::TriggerRegisteredEvent(listener->Event(), name); Events::TriggerExternalEvent(listener->Event(), name);
} }
} }
} }
@ -851,6 +881,9 @@ LaunchDaemon::_AddTargets(BMessage& message)
_SetEvent(target, targetMessage); _SetEvent(target, targetMessage);
_SetEnvironment(target, targetMessage); _SetEnvironment(target, targetMessage);
_AddJobs(target, targetMessage); _AddJobs(target, targetMessage);
if (target->Event() != NULL)
target->Event()->Register(*this);
} }
} }
@ -980,6 +1013,8 @@ LaunchDaemon::_InitJobs(Target* target)
|| job->Condition()->Test(*this)) { || job->Condition()->Test(*this)) {
std::set<BString> dependencies; std::set<BString> dependencies;
status = job->Init(*this, dependencies); status = job->Init(*this, dependencies);
if (status == B_OK && job->Event() != NULL)
status = job->Event()->Register(*this);
} }
} }
@ -1030,18 +1065,20 @@ LaunchDaemon::_LaunchJobs(Target* target, bool forceNow)
/*! Adds the specified \a job to the launch queue /*! Adds the specified \a job to the launch queue
queue, except those that are triggered by events. queue, except those that are triggered by events.
Unless \a forceNow is true, the target is only launched if its events, Unless \c FORCE_NOW is set, the target is only launched if its events,
if any, have been triggered already. if any, have been triggered already.
Calling this method will trigger a demand event. Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
been set.
*/ */
void void
LaunchDaemon::_LaunchJob(Job* job, uint32 options) LaunchDaemon::_LaunchJob(Job* job, uint32 options)
{ {
if (job == NULL || job->IsLaunched() || ((options & FORCE_NOW) == 0 if (job == NULL || (job->IsService() && job->IsLaunched())
&& (!job->EventHasTriggered() || !job->CheckCondition(*this) || ((options & FORCE_NOW) == 0
|| ((options & TRIGGER_DEMAND) != 0 && (!job->EventHasTriggered() || !job->CheckCondition(*this)
&& Events::TriggerDemand(job->Event()))))) { || ((options & TRIGGER_DEMAND) != 0
&& Events::TriggerDemand(job->Event()))))) {
return; return;
} }
@ -1160,7 +1197,7 @@ LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event,
for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
iterator++) { iterator++) {
Job* job = iterator->second; Job* job = iterator->second;
if (Events::ResolveRegisteredEvent(job->Event(), name)) if (Events::ResolveExternalEvent(job->Event(), name, event->Flags()))
event->AddListener(job); event->AddListener(job);
} }
} }
@ -1175,7 +1212,8 @@ LaunchDaemon::_ResolveExternalEvents(BaseJob* job)
for (EventMap::iterator iterator = fEvents.begin(); for (EventMap::iterator iterator = fEvents.begin();
iterator != fEvents.end(); iterator++) { iterator != fEvents.end(); iterator++) {
ExternalEventSource* event = iterator->second; ExternalEventSource* event = iterator->second;
if (Events::ResolveRegisteredEvent(job->Event(), event->Name())) if (Events::ResolveExternalEvent(job->Event(), event->Name(),
event->Flags()))
event->AddListener(job); event->AddListener(job);
} }
} }

View File

@ -81,7 +81,7 @@ AutoMounter::AutoMounter()
BDiskDeviceRoster().StartWatching(this, BDiskDeviceRoster().StartWatching(this,
B_DEVICE_REQUEST_DEVICE | B_DEVICE_REQUEST_DEVICE_LIST); B_DEVICE_REQUEST_DEVICE | B_DEVICE_REQUEST_DEVICE_LIST);
BLaunchRoster().RegisterEvent(this, kInitialMountEvent); BLaunchRoster().RegisterEvent(this, kInitialMountEvent, B_STICKY_EVENT);
} }