2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
7 //! kernel-side implementation of the messaging service
12 #include <AutoDeleter.h>
13 #include <KernelExport.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
30 static MessagingService
*sMessagingService
= NULL
;
32 static const int32 kMessagingAreaSize
= B_PAGE_SIZE
* 4;
35 // #pragma mark - MessagingArea
38 MessagingArea::MessagingArea()
43 MessagingArea::~MessagingArea()
51 MessagingArea::Create(sem_id lockSem
, sem_id counterSem
)
53 // allocate the object on the heap
54 MessagingArea
*area
= new(nothrow
) MessagingArea
;
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
);
67 // finish the initialization of the object
68 area
->fSize
= kMessagingAreaSize
;
69 area
->fLockSem
= lockSem
;
70 area
->fCounterSem
= counterSem
;
71 area
->fNextArea
= NULL
;
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;
92 MessagingArea::CheckCommandSize(int32 dataSize
)
94 int32 size
= sizeof(messaging_command
) + dataSize
;
97 && size
<= kMessagingAreaSize
- (int32
)sizeof(messaging_area_header
));
102 MessagingArea::Lock()
104 // benaphore-like locking
105 if (atomic_add(&fHeader
->lock_counter
, 1) == 0)
108 return (acquire_sem(fLockSem
) == B_OK
);
113 MessagingArea::Unlock()
115 if (atomic_add(&fHeader
->lock_counter
, -1) > 1)
116 release_sem(fLockSem
);
121 MessagingArea::ID() const
128 MessagingArea::Size() const
135 MessagingArea::IsEmpty() const
137 return fHeader
->command_count
== 0;
142 MessagingArea::AllocateCommand(uint32 commandWhat
, int32 dataSize
,
145 int32 size
= sizeof(messaging_command
) + dataSize
;
147 if (dataSize
< 0 || size
> fSize
- (int32
)sizeof(messaging_area_header
))
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
155 wasEmpty
= (fHeader
->command_count
== 0);
157 commandOffset
= startOffset
;
160 fHeader
->command_count
++;
161 fHeader
->first_command
= fHeader
->last_command
= commandOffset
;
163 int32 firstCommandOffset
= fHeader
->first_command
;
164 int32 lastCommandOffset
= fHeader
->last_command
;
165 int32 firstCommandSize
;
166 int32 lastCommandSize
;
167 messaging_command
*firstCommand
= _CheckCommand(firstCommandOffset
,
169 messaging_command
*lastCommand
= _CheckCommand(lastCommandOffset
,
171 if (!firstCommand
|| !lastCommand
) {
172 // something has been screwed up
176 // find space for the command
177 if (firstCommandOffset
<= lastCommandOffset
) {
179 // try to allocate after the last command
180 if (size
<= fSize
- (lastCommandOffset
+ lastCommandSize
)) {
181 commandOffset
= (lastCommandOffset
+ lastCommandSize
);
183 // is there enough space before the first command?
184 if (size
> firstCommandOffset
- startOffset
)
186 commandOffset
= startOffset
;
189 // wrapped: we can only allocate between the last and the first
191 commandOffset
= lastCommandOffset
+ lastCommandSize
;
192 if (size
> firstCommandOffset
- commandOffset
)
196 // update the header and the last command
197 fHeader
->command_count
++;
198 lastCommand
->next_command
= fHeader
->last_command
= commandOffset
;
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
;
213 MessagingArea::CommitCommand()
215 // TODO: If invoked while locked, we should supply B_DO_NOT_RESCHEDULE.
216 release_sem(fCounterSem
);
221 MessagingArea::SetNextArea(MessagingArea
*area
)
224 fHeader
->next_kernel_area
= (fNextArea
? fNextArea
->ID() : -1);
229 MessagingArea::NextArea() const
236 MessagingArea::_CheckCommand(int32 offset
, int32
&size
)
239 if (offset
< (int32
)sizeof(messaging_area_header
)
240 || offset
+ (int32
)sizeof(messaging_command
) > fSize
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
))
250 size
= (size
+ 3) & ~0x3; // align
251 if (offset
+ size
> fSize
)
258 // #pragma mark - MessagingService
261 MessagingService::MessagingService()
266 recursive_lock_init(&fLock
, "messaging service");
270 MessagingService::~MessagingService()
272 // Should actually never be called. Once created the service stays till the
278 MessagingService::InitCheck() const
285 MessagingService::Lock()
287 return recursive_lock_lock(&fLock
) == B_OK
;
292 MessagingService::Unlock()
294 recursive_lock_unlock(&fLock
);
299 MessagingService::RegisterService(sem_id lockSem
, sem_id counterSem
,
302 // check, if a service is already registered
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
;
314 error
= get_sem_info(lockSem
, &lockSemInfo
);
316 sem_info counterSemInfo
;
318 error
= get_sem_info(counterSem
, &counterSemInfo
);
323 if (threadInfo
.team
!= lockSemInfo
.team
324 || threadInfo
.team
!= counterSemInfo
.team
) {
329 fFirstArea
= fLastArea
= MessagingArea::Create(lockSem
, counterSem
);
333 areaID
= fFirstArea
->ID();
334 fFirstArea
->Unlock();
336 // store the server team and the semaphores
337 fServerTeam
= threadInfo
.team
;
339 fCounterSem
= counterSem
;
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
);
354 if (threadInfo
.team
!= fServerTeam
)
359 MessagingArea
*area
= fFirstArea
;
360 fFirstArea
= area
->NextArea();
365 // unset the other members
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)
383 int32 dataSize
= sizeof(messaging_command_send_message
)
384 + targetCount
* sizeof(messaging_target
) + messageSize
;
386 // allocate space for the command
390 status_t error
= _AllocateCommand(MESSAGING_COMMAND_SEND_MESSAGE
, dataSize
,
391 area
, data
, wasEmpty
);
393 PRINT(("MessagingService::SendMessage(): Failed to allocate space for "
394 "send message command.\n"));
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
);
411 area
->CommitCommand();
418 MessagingService::_AllocateCommand(int32 commandWhat
, int32 size
,
419 MessagingArea
*&area
, void *&data
, bool &wasEmpty
)
424 if (!MessagingArea::CheckCommandSize(size
))
427 // delete the discarded areas (save one)
428 ObjectDeleter
<MessagingArea
> discardedAreaDeleter
;
429 MessagingArea
*discardedArea
= NULL
;
431 while (fFirstArea
!= fLastArea
) {
434 if (!area
->IsEmpty()) {
439 PRINT(("MessagingService::_AllocateCommand(): Discarding area: %p\n",
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
451 data
= area
->AllocateCommand(commandWhat
, size
, wasEmpty
);
454 // not enough space in the last area: create a new area or reuse a
457 area
= discardedAreaDeleter
.Detach();
459 PRINT(("MessagingService::_AllocateCommand(): Not enough space "
460 "left in current area. Recycling discarded one: %p\n", area
));
462 area
= MessagingArea::Create(fLockSem
, fCounterSem
);
463 PRINT(("MessagingService::_AllocateCommand(): Not enough space "
464 "left in current area. Allocated new one: %p\n", area
));
472 fLastArea
->SetNextArea(area
);
476 // allocate space for the command
477 data
= area
->AllocateCommand(commandWhat
, size
, wasEmpty
);
480 // that should never happen
490 // #pragma mark - kernel private
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
)
501 if (!sMessagingService
->Lock())
504 status_t error
= sMessagingService
->SendMessage(message
, messageSize
,
505 targets
, targetCount
);
507 sMessagingService
->Unlock();
514 send_message(const KMessage
*message
, const messaging_target
*targets
,
520 return send_message(message
->Buffer(), message
->ContentSize(), targets
,
526 init_messaging_service()
528 static char buffer
[sizeof(MessagingService
)];
530 if (!sMessagingService
)
531 sMessagingService
= new(buffer
) MessagingService
;
533 status_t error
= sMessagingService
->InitCheck();
537 dprintf("ERROR: Failed to init messaging service: %s\n",
539 sMessagingService
->~MessagingService();
540 sMessagingService
= NULL
;
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
558 - The ID of the kernel area used for communication, if everything went fine,
559 - an error code otherwise.
562 _user_register_messaging_service(sem_id lockSem
, sem_id counterSem
)
564 // check, if init_messaging_service() has been called yet
565 if (!sMessagingService
)
568 if (!sMessagingService
->Lock())
572 status_t error
= sMessagingService
->RegisterService(lockSem
, counterSem
,
575 sMessagingService
->Unlock();
577 return (error
!= B_OK
? error
: areaID
);
582 _user_unregister_messaging_service()
584 // check, if init_messaging_service() has been called yet
585 if (!sMessagingService
)
588 if (!sMessagingService
->Lock())
591 status_t error
= sMessagingService
->UnregisterService();
593 sMessagingService
->Unlock();