BPicture: Fix archive constructor.
[haiku.git] / src / system / kernel / messaging / MessagingService.cpp
blob728a31eb5b1fe2c61e15877fdfc8c16916be9e2d
1 /*
2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 //! kernel-side implementation of the messaging service
10 #include <new>
12 #include <AutoDeleter.h>
13 #include <KernelExport.h>
14 #include <KMessage.h>
15 #include <messaging.h>
16 #include <MessagingServiceDefs.h>
18 #include "MessagingService.h"
20 //#define TRACE_MESSAGING_SERVICE
21 #ifdef TRACE_MESSAGING_SERVICE
22 # define PRINT(x) dprintf x
23 #else
24 # define PRINT(x) ;
25 #endif
28 using namespace std;
30 static MessagingService *sMessagingService = NULL;
32 static const int32 kMessagingAreaSize = B_PAGE_SIZE * 4;
35 // #pragma mark - MessagingArea
38 MessagingArea::MessagingArea()
43 MessagingArea::~MessagingArea()
45 if (fID >= 0)
46 delete_area(fID);
50 MessagingArea *
51 MessagingArea::Create(sem_id lockSem, sem_id counterSem)
53 // allocate the object on the heap
54 MessagingArea *area = new(nothrow) MessagingArea;
55 if (!area)
56 return NULL;
58 // create the area
59 area->fID = create_area("messaging", (void**)&area->fHeader,
60 B_ANY_KERNEL_ADDRESS, kMessagingAreaSize, B_FULL_LOCK,
61 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
62 if (area->fID < 0) {
63 delete area;
64 return NULL;
67 // finish the initialization of the object
68 area->fSize = kMessagingAreaSize;
69 area->fLockSem = lockSem;
70 area->fCounterSem = counterSem;
71 area->fNextArea = NULL;
72 area->InitHeader();
74 return area;
78 void
79 MessagingArea::InitHeader()
81 fHeader->lock_counter = 1; // create locked
82 fHeader->size = fSize;
83 fHeader->kernel_area = fID;
84 fHeader->next_kernel_area = (fNextArea ? fNextArea->ID() : -1);
85 fHeader->command_count = 0;
86 fHeader->first_command = 0;
87 fHeader->last_command = 0;
91 bool
92 MessagingArea::CheckCommandSize(int32 dataSize)
94 int32 size = sizeof(messaging_command) + dataSize;
96 return (dataSize >= 0
97 && size <= kMessagingAreaSize - (int32)sizeof(messaging_area_header));
101 bool
102 MessagingArea::Lock()
104 // benaphore-like locking
105 if (atomic_add(&fHeader->lock_counter, 1) == 0)
106 return true;
108 return (acquire_sem(fLockSem) == B_OK);
112 void
113 MessagingArea::Unlock()
115 if (atomic_add(&fHeader->lock_counter, -1) > 1)
116 release_sem(fLockSem);
120 area_id
121 MessagingArea::ID() const
123 return fID;
127 int32
128 MessagingArea::Size() const
130 return fSize;
134 bool
135 MessagingArea::IsEmpty() const
137 return fHeader->command_count == 0;
141 void *
142 MessagingArea::AllocateCommand(uint32 commandWhat, int32 dataSize,
143 bool &wasEmpty)
145 int32 size = sizeof(messaging_command) + dataSize;
147 if (dataSize < 0 || size > fSize - (int32)sizeof(messaging_area_header))
148 return NULL;
150 // the area is used as a ring buffer
151 int32 startOffset = sizeof(messaging_area_header);
153 // the simple case first: the area is empty
154 int32 commandOffset;
155 wasEmpty = (fHeader->command_count == 0);
156 if (wasEmpty) {
157 commandOffset = startOffset;
159 // update the header
160 fHeader->command_count++;
161 fHeader->first_command = fHeader->last_command = commandOffset;
162 } else {
163 int32 firstCommandOffset = fHeader->first_command;
164 int32 lastCommandOffset = fHeader->last_command;
165 int32 firstCommandSize;
166 int32 lastCommandSize;
167 messaging_command *firstCommand = _CheckCommand(firstCommandOffset,
168 firstCommandSize);
169 messaging_command *lastCommand = _CheckCommand(lastCommandOffset,
170 lastCommandSize);
171 if (!firstCommand || !lastCommand) {
172 // something has been screwed up
173 return NULL;
176 // find space for the command
177 if (firstCommandOffset <= lastCommandOffset) {
178 // not wrapped
179 // try to allocate after the last command
180 if (size <= fSize - (lastCommandOffset + lastCommandSize)) {
181 commandOffset = (lastCommandOffset + lastCommandSize);
182 } else {
183 // is there enough space before the first command?
184 if (size > firstCommandOffset - startOffset)
185 return NULL;
186 commandOffset = startOffset;
188 } else {
189 // wrapped: we can only allocate between the last and the first
190 // command
191 commandOffset = lastCommandOffset + lastCommandSize;
192 if (size > firstCommandOffset - commandOffset)
193 return NULL;
196 // update the header and the last command
197 fHeader->command_count++;
198 lastCommand->next_command = fHeader->last_command = commandOffset;
201 // init the command
202 messaging_command *command
203 = (messaging_command*)((char*)fHeader + commandOffset);
204 command->next_command = 0;
205 command->command = commandWhat;
206 command->size = size;
208 return command->data;
212 void
213 MessagingArea::CommitCommand()
215 // TODO: If invoked while locked, we should supply B_DO_NOT_RESCHEDULE.
216 release_sem(fCounterSem);
220 void
221 MessagingArea::SetNextArea(MessagingArea *area)
223 fNextArea = area;
224 fHeader->next_kernel_area = (fNextArea ? fNextArea->ID() : -1);
228 MessagingArea *
229 MessagingArea::NextArea() const
231 return fNextArea;
235 messaging_command *
236 MessagingArea::_CheckCommand(int32 offset, int32 &size)
238 // check offset
239 if (offset < (int32)sizeof(messaging_area_header)
240 || offset + (int32)sizeof(messaging_command) > fSize
241 || (offset & 0x3)) {
242 return NULL;
245 // get and check size
246 messaging_command *command = (messaging_command*)((char*)fHeader + offset);
247 size = command->size;
248 if (size < (int32)sizeof(messaging_command))
249 return NULL;
250 size = (size + 3) & ~0x3; // align
251 if (offset + size > fSize)
252 return NULL;
254 return command;
258 // #pragma mark - MessagingService
261 MessagingService::MessagingService()
263 fFirstArea(NULL),
264 fLastArea(NULL)
266 recursive_lock_init(&fLock, "messaging service");
270 MessagingService::~MessagingService()
272 // Should actually never be called. Once created the service stays till the
273 // bitter end.
277 status_t
278 MessagingService::InitCheck() const
280 return B_OK;
284 bool
285 MessagingService::Lock()
287 return recursive_lock_lock(&fLock) == B_OK;
291 void
292 MessagingService::Unlock()
294 recursive_lock_unlock(&fLock);
298 status_t
299 MessagingService::RegisterService(sem_id lockSem, sem_id counterSem,
300 area_id &areaID)
302 // check, if a service is already registered
303 if (fFirstArea)
304 return B_BAD_VALUE;
306 status_t error = B_OK;
308 // check, if the semaphores are valid and belong to the calling team
309 thread_info threadInfo;
310 error = get_thread_info(find_thread(NULL), &threadInfo);
312 sem_info lockSemInfo;
313 if (error == B_OK)
314 error = get_sem_info(lockSem, &lockSemInfo);
316 sem_info counterSemInfo;
317 if (error == B_OK)
318 error = get_sem_info(counterSem, &counterSemInfo);
320 if (error != B_OK)
321 return error;
323 if (threadInfo.team != lockSemInfo.team
324 || threadInfo.team != counterSemInfo.team) {
325 return B_BAD_VALUE;
328 // create an area
329 fFirstArea = fLastArea = MessagingArea::Create(lockSem, counterSem);
330 if (!fFirstArea)
331 return B_NO_MEMORY;
333 areaID = fFirstArea->ID();
334 fFirstArea->Unlock();
336 // store the server team and the semaphores
337 fServerTeam = threadInfo.team;
338 fLockSem = lockSem;
339 fCounterSem = counterSem;
341 return B_OK;
345 status_t
346 MessagingService::UnregisterService()
348 // check, if the team calling this function is indeed the server team
349 thread_info threadInfo;
350 status_t error = get_thread_info(find_thread(NULL), &threadInfo);
351 if (error != B_OK)
352 return error;
354 if (threadInfo.team != fServerTeam)
355 return B_BAD_VALUE;
357 // delete all areas
358 while (fFirstArea) {
359 MessagingArea *area = fFirstArea;
360 fFirstArea = area->NextArea();
361 delete area;
363 fLastArea = NULL;
365 // unset the other members
366 fLockSem = -1;
367 fCounterSem = -1;
368 fServerTeam = -1;
370 return B_OK;
374 status_t
375 MessagingService::SendMessage(const void *message, int32 messageSize,
376 const messaging_target *targets, int32 targetCount)
378 PRINT(("MessagingService::SendMessage(%p, %ld, %p, %ld)\n", message,
379 messageSize, targets, targetCount));
380 if (!message || messageSize <= 0 || !targets || targetCount <= 0)
381 return B_BAD_VALUE;
383 int32 dataSize = sizeof(messaging_command_send_message)
384 + targetCount * sizeof(messaging_target) + messageSize;
386 // allocate space for the command
387 MessagingArea *area;
388 void *data;
389 bool wasEmpty;
390 status_t error = _AllocateCommand(MESSAGING_COMMAND_SEND_MESSAGE, dataSize,
391 area, data, wasEmpty);
392 if (error != B_OK) {
393 PRINT(("MessagingService::SendMessage(): Failed to allocate space for "
394 "send message command.\n"));
395 return error;
397 PRINT((" Allocated space for send message command: area: %p, data: %p, "
398 "wasEmpty: %d\n", area, data, wasEmpty));
400 // prepare the command
401 messaging_command_send_message *command
402 = (messaging_command_send_message*)data;
403 command->message_size = messageSize;
404 command->target_count = targetCount;
405 memcpy(command->targets, targets, sizeof(messaging_target) * targetCount);
406 memcpy((char*)command + (dataSize - messageSize), message, messageSize);
408 // shoot
409 area->Unlock();
410 if (wasEmpty)
411 area->CommitCommand();
413 return B_OK;
417 status_t
418 MessagingService::_AllocateCommand(int32 commandWhat, int32 size,
419 MessagingArea *&area, void *&data, bool &wasEmpty)
421 if (!fFirstArea)
422 return B_NO_INIT;
424 if (!MessagingArea::CheckCommandSize(size))
425 return B_BAD_VALUE;
427 // delete the discarded areas (save one)
428 ObjectDeleter<MessagingArea> discardedAreaDeleter;
429 MessagingArea *discardedArea = NULL;
431 while (fFirstArea != fLastArea) {
432 area = fFirstArea;
433 area->Lock();
434 if (!area->IsEmpty()) {
435 area->Unlock();
436 break;
439 PRINT(("MessagingService::_AllocateCommand(): Discarding area: %p\n",
440 area));
442 fFirstArea = area->NextArea();
443 area->SetNextArea(NULL);
444 discardedArea = area;
445 discardedAreaDeleter.SetTo(area);
448 // allocate space for the command in the last area
449 area = fLastArea;
450 area->Lock();
451 data = area->AllocateCommand(commandWhat, size, wasEmpty);
453 if (!data) {
454 // not enough space in the last area: create a new area or reuse a
455 // discarded one
456 if (discardedArea) {
457 area = discardedAreaDeleter.Detach();
458 area->InitHeader();
459 PRINT(("MessagingService::_AllocateCommand(): Not enough space "
460 "left in current area. Recycling discarded one: %p\n", area));
461 } else {
462 area = MessagingArea::Create(fLockSem, fCounterSem);
463 PRINT(("MessagingService::_AllocateCommand(): Not enough space "
464 "left in current area. Allocated new one: %p\n", area));
466 if (!area) {
467 fLastArea->Unlock();
468 return B_NO_MEMORY;
471 // add the new area
472 fLastArea->SetNextArea(area);
473 fLastArea->Unlock();
474 fLastArea = area;
476 // allocate space for the command
477 data = area->AllocateCommand(commandWhat, size, wasEmpty);
479 if (!data) {
480 // that should never happen
481 area->Unlock();
482 return B_NO_MEMORY;
486 return B_OK;
490 // #pragma mark - kernel private
493 status_t
494 send_message(const void *message, int32 messageSize,
495 const messaging_target *targets, int32 targetCount)
497 // check, if init_messaging_service() has been called yet
498 if (!sMessagingService)
499 return B_NO_INIT;
501 if (!sMessagingService->Lock())
502 return B_BAD_VALUE;
504 status_t error = sMessagingService->SendMessage(message, messageSize,
505 targets, targetCount);
507 sMessagingService->Unlock();
509 return error;
513 status_t
514 send_message(const KMessage *message, const messaging_target *targets,
515 int32 targetCount)
517 if (!message)
518 return B_BAD_VALUE;
520 return send_message(message->Buffer(), message->ContentSize(), targets,
521 targetCount);
525 status_t
526 init_messaging_service()
528 static char buffer[sizeof(MessagingService)];
530 if (!sMessagingService)
531 sMessagingService = new(buffer) MessagingService;
533 status_t error = sMessagingService->InitCheck();
535 // cleanup on error
536 if (error != B_OK) {
537 dprintf("ERROR: Failed to init messaging service: %s\n",
538 strerror(error));
539 sMessagingService->~MessagingService();
540 sMessagingService = NULL;
543 return error;
547 // #pragma mark - syscalls
550 /** \brief Called by the userland server to register itself as a messaging
551 service for the kernel.
552 \param lockingSem A semaphore used for locking the shared data. Semaphore
553 counter must be initialized to 0.
554 \param counterSem A semaphore released every time the kernel pushes a
555 command into an empty area. Semaphore counter must be initialized
556 to 0.
557 \return
558 - The ID of the kernel area used for communication, if everything went fine,
559 - an error code otherwise.
561 area_id
562 _user_register_messaging_service(sem_id lockSem, sem_id counterSem)
564 // check, if init_messaging_service() has been called yet
565 if (!sMessagingService)
566 return B_NO_INIT;
568 if (!sMessagingService->Lock())
569 return B_BAD_VALUE;
571 area_id areaID;
572 status_t error = sMessagingService->RegisterService(lockSem, counterSem,
573 areaID);
575 sMessagingService->Unlock();
577 return (error != B_OK ? error : areaID);
581 status_t
582 _user_unregister_messaging_service()
584 // check, if init_messaging_service() has been called yet
585 if (!sMessagingService)
586 return B_NO_INIT;
588 if (!sMessagingService->Lock())
589 return B_BAD_VALUE;
591 status_t error = sMessagingService->UnregisterService();
593 sMessagingService->Unlock();
595 return error;