2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. All rights reserved.
3 * Distributed under the terms of the MIT License.
16 #include "MessageDeliverer.h"
17 #include "MessagingService.h"
22 // sService -- the singleton instance
23 MessagingService
*MessagingService::sService
= NULL
;
25 /*! \class MessagingArea
26 \brief Represents an area of the messaging service shared between kernel
29 The main purpose of the class is to retrieve (and remove) commands from
34 MessagingArea::MessagingArea()
39 MessagingArea::~MessagingArea()
47 MessagingArea::Create(area_id kernelAreaID
, sem_id lockSem
, sem_id counterSem
,
48 MessagingArea
*&_area
)
50 // allocate the object on the heap
51 MessagingArea
*area
= new(nothrow
) MessagingArea
;
55 // clone the kernel area
56 area_id areaID
= clone_area("messaging", (void**)&area
->fHeader
,
57 B_ANY_ADDRESS
, B_READ_AREA
| B_WRITE_AREA
, kernelAreaID
);
63 // finish the initialization of the object
65 area
->fSize
= area
->fHeader
->size
;
66 area
->fLockSem
= lockSem
;
67 area
->fCounterSem
= counterSem
;
68 area
->fNextArea
= NULL
;
78 // benaphore-like locking
79 if (atomic_add(&fHeader
->lock_counter
, 1) == 0)
82 return (acquire_sem(fLockSem
) == B_OK
);
87 MessagingArea::Unlock()
89 if (atomic_add(&fHeader
->lock_counter
, -1) > 1)
90 release_sem(fLockSem
);
95 MessagingArea::ID() const
102 MessagingArea::Size() const
109 MessagingArea::CountCommands() const
111 return fHeader
->command_count
;
115 const messaging_command
*
116 MessagingArea::PopCommand()
118 if (fHeader
->command_count
== 0)
122 messaging_command
*command
123 = (messaging_command
*)((char*)fHeader
+ fHeader
->first_command
);
125 // remove it from the area
126 // (as long as the area is still locked, noone will overwrite the contents)
127 if (--fHeader
->command_count
== 0)
128 fHeader
->first_command
= fHeader
->last_command
= 0;
130 fHeader
->first_command
= command
->next_command
;
137 MessagingArea::Discard()
144 MessagingArea::NextKernelAreaID() const
146 return fHeader
->next_kernel_area
;
151 MessagingArea::SetNextArea(MessagingArea
*area
)
158 MessagingArea::NextArea() const
167 MessagingCommandHandler::MessagingCommandHandler()
172 MessagingCommandHandler::~MessagingCommandHandler()
179 // DefaultSendCommandHandler
180 class MessagingService::DefaultSendCommandHandler
181 : public MessagingCommandHandler
{
183 virtual void HandleMessagingCommand(uint32 _command
, const void *data
,
186 const messaging_command_send_message
*sendData
187 = (const messaging_command_send_message
*)data
;
188 const void *messageData
= (uint8
*)data
189 + sizeof(messaging_command_send_message
)
190 + sizeof(messaging_target
) * sendData
->target_count
;
192 DefaultMessagingTargetSet
set(sendData
->targets
,
193 sendData
->target_count
);
194 MessageDeliverer::Default()->DeliverMessage(messageData
,
195 sendData
->message_size
, set
);
200 struct MessagingService::CommandHandlerMap
201 : map
<uint32
, MessagingCommandHandler
*> {
205 /*! \class MessagingService
206 \brief Userland implementation of the kernel -> userland messaging service.
208 This service provides a way for the kernel to send BMessages (usually
209 notification (e.g. node monitoring) messages) to userland applications.
211 The kernel could write the messages directly to the respective target ports,
212 but this has the disadvantage, that a message needs to be dropped, if the
213 port is full at the moment of sending. By transferring the message to the
214 registrar, it is possible to use the MessageDeliverer which retries sending
215 messages on full ports.
217 The message transfer is implemented via areas shared between kernel
218 and registrar. By default one area is used as a ring buffer. The kernel
219 adds messages to it, the registrar removes them. If the area is full, the
220 kernel creates a new one and adds it to the area list.
222 While the service is called `messaging service' and we were speaking of
223 `messages' being passed through the areas, the service is actually more
224 general. In fact `commands' are passed through the areas. Currently the
225 only implemented command type is to send a message, but it is very easy
226 to add further command types (e.g. one for alerting the user in case of
229 The MessagingService maintains a mapping of command types to command
230 handlers (MessagingCommandHandler, which perform the actual processing
231 of the commands), that can be altered via
232 MessagingService::SetCommandHandler().
236 MessagingService::MessagingService()
237 : fLock("messaging service"),
241 fCommandHandlers(NULL
),
242 fCommandProcessor(-1),
248 MessagingService::~MessagingService()
253 delete_sem(fLockSem
);
254 if (fCounterSem
>= 0)
255 delete_sem(fCounterSem
);
257 if (fCommandProcessor
>= 0) {
259 wait_for_thread(fCommandProcessor
, &result
);
262 delete fCommandHandlers
;
269 MessagingService::Init()
271 // create the semaphores
272 fLockSem
= create_sem(0, "messaging lock");
276 fCounterSem
= create_sem(0, "messaging counter");
280 // create the command handler map
281 fCommandHandlers
= new(nothrow
) CommandHandlerMap
;
282 if (!fCommandHandlers
)
285 // spawn the command processor
286 fCommandProcessor
= spawn_thread(MessagingService::_CommandProcessorEntry
,
287 "messaging command processor", B_DISPLAY_PRIORITY
, this);
288 if (fCommandProcessor
< 0)
289 return fCommandProcessor
;
291 // register with the kernel
292 area_id areaID
= _kern_register_messaging_service(fLockSem
, fCounterSem
);
297 status_t error
= MessagingArea::Create(areaID
, fLockSem
, fCounterSem
,
300 _kern_unregister_messaging_service();
304 // resume the command processor
305 resume_thread(fCommandProcessor
);
307 // install the default send message command handler
308 MessagingCommandHandler
*handler
= new(nothrow
) DefaultSendCommandHandler
;
311 SetCommandHandler(MESSAGING_COMMAND_SEND_MESSAGE
, handler
);
318 MessagingService::CreateDefault()
323 // create the service
324 MessagingService
*service
= new(nothrow
) MessagingService
;
329 status_t error
= service
->Init();
341 MessagingService::DeleteDefault()
351 MessagingService::Default()
358 MessagingService::SetCommandHandler(uint32 command
,
359 MessagingCommandHandler
*handler
)
364 (*fCommandHandlers
)[command
] = handler
;
366 // no handler: remove and existing entry
367 CommandHandlerMap::iterator it
= fCommandHandlers
->find(command
);
368 if (it
!= fCommandHandlers
->end())
369 fCommandHandlers
->erase(it
);
373 // _GetCommandHandler
374 MessagingCommandHandler
*
375 MessagingService::_GetCommandHandler(uint32 command
) const
379 CommandHandlerMap::iterator it
= fCommandHandlers
->find(command
);
380 return (it
!= fCommandHandlers
->end() ? it
->second
: NULL
);
383 // _CommandProcessorEntry
385 MessagingService::_CommandProcessorEntry(void *data
)
387 return ((MessagingService
*)data
)->_CommandProcessor();
392 MessagingService::_CommandProcessor()
394 bool commandWaiting
= false;
395 while (!fTerminating
) {
396 // wait for the next command
397 if (!commandWaiting
) {
398 status_t error
= acquire_sem(fCounterSem
);
402 commandWaiting
= false;
404 // get it from the first area
405 MessagingArea
*area
= fFirstArea
;
407 while (area
->CountCommands() > 0) {
408 const messaging_command
*command
= area
->PopCommand();
410 // something's seriously wrong
411 ERROR("MessagingService::_CommandProcessor(): area %p (%"
412 B_PRId32
") has command count %" B_PRId32
", but doesn't "
413 "return any more commands.", area
, area
->ID(),
414 area
->CountCommands());
417 PRINT("MessagingService::_CommandProcessor(): got command %" B_PRIu32
"\n",
420 // dispatch the command
421 MessagingCommandHandler
*handler
422 = _GetCommandHandler(command
->command
);
424 handler
->HandleMessagingCommand(command
->command
, command
->data
,
425 command
->size
- sizeof(messaging_command
));
427 WARNING("MessagingService::_CommandProcessor(): No handler "
428 "found for command %" B_PRIu32
"\n", command
->command
);
432 // there is a new area we don't know yet
433 if (!area
->NextArea() && area
->NextKernelAreaID() >= 0) {
435 MessagingArea
*nextArea
;
436 status_t error
= MessagingArea::Create(area
->NextKernelAreaID(),
437 fLockSem
, fCounterSem
, nextArea
);
439 area
->SetNextArea(nextArea
);
440 commandWaiting
= true;
442 // Bad, but what can we do?
443 ERROR("MessagingService::_CommandProcessor(): Failed to clone "
444 "kernel area %" B_PRId32
": %s\n", area
->NextKernelAreaID(),
450 // if the current area is empty and there is a next one, we discard the
452 if (area
->NextArea() && area
->CountCommands() == 0) {
453 fFirstArea
= area
->NextArea();