VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/ipc/ipcd/shared/src/ipcMessageNew.h

Last change on this file was 103472, checked in by vboxsync, 3 months ago

libs/xpcom: Rewrite the IPC client side to reduce the number of memory allocations, bugref:10598

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Mozilla IPC.
15 *
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Darin Fisher <darin@netscape.com>
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#ifndef ipcMessageNew_h__
39#define ipcMessageNew_h__
40
41#include <iprt/assert.h>
42#include <iprt/assertcompile.h>
43#include <iprt/cdefs.h>
44#include <iprt/err.h>
45#include <iprt/list.h>
46#include <iprt/types.h>
47#include <iprt/mem.h>
48#include <iprt/sg.h>
49
50#include "nsID.h"
51
52//
53// ipc message format:
54//
55// +------------------------------------+
56// | DWORD : length |
57// +------------------+-----------------+
58// | WORD : version | WORD : flags |
59// +------------------+-----------------+
60// | nsID : target |
61// +------------------------------------+
62// | data |
63// +------------------------------------+
64//
65// header is 24 bytes. flags are defined below. default value of flags is
66// zero. protocol implementations should ignore unrecognized flags. target
67// is a 16 byte UUID indicating the intended receiver of this message.
68//
69typedef struct IPCMSGHDR
70{
71 /** Size of the message in bytes, including this header. */
72 uint32_t cbMsg;
73 /** The version of the IPC message header. */
74 uint16_t u16Version;
75 /** Flags for the message. */
76 uint16_t u16Flags;
77 /** The target ID for this message. */
78 nsID idTarget;
79} IPCMSGHDR;
80AssertCompileSize(IPCMSGHDR, 4 + 2 + 2 + 16);
81/** Pointer to the IPC message header. */
82typedef IPCMSGHDR *PIPCMSGHDR;
83/** Pointer to a const IPC message header. */
84typedef const IPCMSGHDR *PCIPCMSGHDR;
85
86/** IPC message version. */
87#define IPC_MSG_HDR_VERSION UINT16_C(0x1)
88
89//
90// the IPC message protocol supports synchronous messages. these messages can
91// only be sent from a client to the daemon. a daemon module cannot send a
92// synchronous message. the client sets the SYNC_QUERY flag to indicate that
93// it is expecting a response with the SYNC_REPLY flag set.
94//
95#define IPC_MSG_HDR_FLAG_SYNC_QUERY RT_BIT(0)
96#define IPC_MSG_HDR_FLAG_SYNC_REPLY RT_BIT(1)
97
98//
99// a special flag to prevent repeated processing of the same message by two
100// or more selectors when walking through the queue of pending messages
101// in WaitTarget().
102//
103#define IPC_MSG_HDR_FLAG_IN_PROCESS RT_BIT(2)
104
105
106/**
107 * IPC message structure.
108 */
109typedef struct IPCMSG
110{
111 /** List node for putting the message onto a list. */
112 RTLISTNODE NdMsg;
113 /** Pointer to the IPC message header. */
114 PIPCMSGHDR pMsgHdr;
115 /** Size of the message buffer in bytes (maximum size of the buffer). */
116 size_t cbBuf;
117 /** Pointer to the message buffer. */
118 uint8_t *pbBuf;
119 /** Offset into the message buffer to append data to. */
120 uint32_t offAppend;
121 /** Flag whether the message is complete and the buffer is therefore readonly right now. */
122 bool fReadonly;
123 /** Flag whether the message is living on the stack and shouldn't be freed. */
124 bool fStack;
125 /** Some metadata which is up to the user to use. */
126 uintptr_t upUser;
127} IPCMSG;
128/** Pointer to an IPC message. */
129typedef IPCMSG *PIPCMSG;
130/** Pointer to a const IPC message. */
131typedef const IPCMSG *PCIPCMSG;
132
133
134DECLINLINE(PIPCMSGHDR) IPCMsgHdrInit(PIPCMSGHDR pMsgHdr, const nsID &target, size_t cbPayload)
135{
136 Assert(cbPayload == (uint32_t)cbPayload);
137
138 pMsgHdr->cbMsg = sizeof(*pMsgHdr) + (uint32_t)cbPayload;
139 pMsgHdr->u16Version = IPC_MSG_HDR_VERSION;
140 pMsgHdr->u16Flags = 0;
141 pMsgHdr->idTarget = target;
142
143 return pMsgHdr;
144}
145
146
147DECLINLINE(void) IPCMsgInitStack(PIPCMSG pThis, const nsID &target, const void *pvData, size_t cbData)
148{
149 pThis->fStack = true;
150 pThis->fReadonly = true;
151 pThis->pbBuf = (uint8_t *)pvData;
152 pThis->cbBuf = sizeof(*pThis->pMsgHdr) + cbData;
153 pThis->pMsgHdr = (PIPCMSGHDR)pThis->pbBuf;
154 pThis->pMsgHdr->cbMsg = sizeof(*pThis->pMsgHdr) + cbData;
155 pThis->pMsgHdr->u16Version = IPC_MSG_HDR_VERSION;
156 pThis->pMsgHdr->u16Flags = 0;
157 pThis->pMsgHdr->idTarget = target;
158}
159
160
161DECLINLINE(int) IPCMsgInit(PIPCMSG pThis, size_t cbBuf)
162{
163 Assert(!pThis->pbBuf);
164
165 pThis->pbBuf = (uint8_t *)RTMemAlloc(sizeof(*pThis->pMsgHdr) + cbBuf);
166 if (RT_UNLIKELY(!pThis->pbBuf))
167 return VERR_NO_MEMORY;
168
169 pThis->cbBuf = sizeof(*pThis->pMsgHdr) + cbBuf;
170 pThis->offAppend = 0;
171 pThis->fReadonly = false;
172 pThis->pMsgHdr = NULL;
173 return VINF_SUCCESS;
174}
175
176
177/**
178 * Allocates a new IPC message structure which can hold the given amount of data initially.
179 *
180 * @returns Pointer to the IPC message structure or NULL if out of memory.
181 * @param cbMsg Size of the payload message buffer in bytes, can be 0 if not known at allocation time.
182 */
183DECLINLINE(PIPCMSG) IPCMsgAlloc(size_t cbMsg)
184{
185 PIPCMSG pThis = (PIPCMSG)RTMemAllocZ(sizeof(*pThis));
186 if (RT_UNLIKELY(!pThis))
187 return NULL;
188
189 pThis->fStack = false;
190
191 if (cbMsg)
192 {
193 pThis->pbBuf = (uint8_t *)RTMemAlloc(sizeof(*pThis->pMsgHdr) + cbMsg);
194 if (RT_UNLIKELY(!pThis->pbBuf))
195 {
196 RTMemFree(pThis);
197 return NULL;
198 }
199
200 pThis->cbBuf = sizeof(*pThis->pMsgHdr) + cbMsg;
201 }
202
203 return pThis;
204}
205
206
207/**
208 * Frees the given message and all associated resources.
209 *
210 * @param pThis The IPC message to free.
211 * @param fFreeStruct Flag whether to free the structure (true) or just the message buffer (false).
212 */
213DECLINLINE(void) IPCMsgFree(PIPCMSG pThis, bool fFreeStruct)
214{
215 /* Stack based messages are never freed. */
216 if (pThis->fStack)
217 return;
218
219 if (pThis->pbBuf)
220 RTMemFree(pThis->pbBuf);
221 pThis->pbBuf = NULL;
222 pThis->pMsgHdr = NULL;
223 pThis->cbBuf = 0;
224 pThis->offAppend = 0;
225 pThis->fReadonly = false;
226
227 if (fFreeStruct)
228 RTMemFree(pThis);
229}
230
231
232/**
233 * Reset the given message structure to accept a new message.
234 *
235 * @param pThis The IPC message to reset.
236 */
237DECLINLINE(void) IPCMsgReset(PIPCMSG pThis)
238{
239 pThis->pMsgHdr = NULL;
240 pThis->offAppend = 0;
241 pThis->fReadonly = false;
242}
243
244
245/**
246 * Returns a pointer to the message data including the header.
247 *
248 * @param pThis The IPC message.
249 */
250DECLINLINE(void *) IPCMsgGetBuf(PCIPCMSG pThis)
251{
252 AssertReturn(pThis->pMsgHdr, NULL);
253
254 return pThis->pMsgHdr;
255}
256
257
258/**
259 * Returns the size of the message in bytes including the header.
260 *
261 * @param pThis The IPC message.
262 */
263DECLINLINE(size_t) IPCMsgGetSize(PCIPCMSG pThis)
264{
265 AssertReturn(pThis->pMsgHdr, 0);
266
267 return pThis->pMsgHdr->cbMsg;
268}
269
270
271/**
272 * Returns the message header of the given message.
273 *
274 * @returns Pointer to the IPC message header.
275 * @param pThis The IPC message.
276 */
277DECLINLINE(PCIPCMSGHDR) IPCMsgGetHdr(PCIPCMSG pThis)
278{
279 Assert(pThis->pMsgHdr);
280 return pThis->pMsgHdr;
281}
282
283
284DECLINLINE(const nsID *) IPCMsgGetTarget(PCIPCMSG pThis)
285{
286 AssertReturn(pThis->pMsgHdr, NULL);
287 return &pThis->pMsgHdr->idTarget;
288}
289
290
291/**
292 * Returns a pointer to the message payload data.
293 *
294 * @param pThis The IPC message.
295 */
296DECLINLINE(void *) IPCMsgGetPayload(PCIPCMSG pThis)
297{
298 AssertReturn(pThis->pMsgHdr, NULL);
299
300 return pThis->pMsgHdr + 1;
301}
302
303
304/**
305 * Returns the size of the message payload in bytes.
306 *
307 * @param pThis The IPC message.
308 */
309DECLINLINE(size_t) IPCMsgGetPayloadSize(PCIPCMSG pThis)
310{
311 AssertReturn(pThis->pMsgHdr, 0);
312
313 return pThis->pMsgHdr->cbMsg - sizeof(*pThis->pMsgHdr);
314}
315
316
317/**
318 * Returns whether the message is readonly (or complete).
319 *
320 * @returns Flag whether the message is readonly.
321 * @param pThis The IPC message.
322 */
323DECLINLINE(bool) IPCMsgIsReadonly(PCIPCMSG pThis)
324{
325 return pThis->fReadonly;
326}
327
328
329/**
330 * Returns whether the given flag is set in the message header.
331 *
332 * @returns true if the flag is set false it is clear.
333 * @param pThis The IPC message.
334 * @param fFlag The flag (or combination) to check.
335 */
336DECLINLINE(bool) IPCMsgIsFlagSet(PCIPCMSG pThis, uint16_t fFlag)
337{
338 return RT_BOOL(pThis->pMsgHdr->u16Flags & fFlag);
339}
340
341
342/**
343 * Sets the given flag in the message header.
344 *
345 * @param pThis The IPC message.
346 * @param fFlag The flag to set.
347 */
348DECLINLINE(void) IPCMsgSetFlag(PIPCMSG pThis, uint16_t fFlag)
349{
350 pThis->pMsgHdr->u16Flags |= fFlag;
351}
352
353
354/**
355 * Clears the given flag in the message header.
356 *
357 * @param pThis The IPC message.
358 * @param fFlag The flag to clear.
359 */
360DECLINLINE(void) IPCMsgClearFlag(PIPCMSG pThis, uint16_t fFlag)
361{
362 pThis->pMsgHdr->u16Flags &= ~fFlag;
363}
364
365
366/**
367 * Init worker for a given message frame.
368 *
369 * @param pThis The message to initialize.
370 * @param target the target for the message.
371 * @param cbTotal Size of the payload in bytes.
372 * @param paSegs Pointer to the segment array making up the message payload.
373 * @param cSegs Number of segments in the array.
374 */
375DECLINLINE(void) ipcMsgInitWorker(PIPCMSG pThis, const nsID &target, size_t cbTotal, PCRTSGSEG paSegs, uint32_t cSegs)
376{
377 pThis->pMsgHdr = (PIPCMSGHDR)pThis->pbBuf;
378 pThis->pMsgHdr->cbMsg = sizeof(*pThis->pMsgHdr) + cbTotal;
379 pThis->pMsgHdr->u16Version = IPC_MSG_HDR_VERSION;
380 pThis->pMsgHdr->u16Flags = 0;
381 pThis->pMsgHdr->idTarget = target;
382
383 uint8_t *pb = (uint8_t *)(pThis->pMsgHdr + 1);
384 for (uint32_t i = 0; i < cSegs; i++)
385 {
386 memcpy(pb, paSegs[i].pvSeg, paSegs[i].cbSeg);
387 pb += paSegs[i].cbSeg;
388 }
389
390 pThis->fReadonly = true;
391}
392
393
394/**
395 * Allocates a new message with the given target and content.
396 *
397 * @returns Pointer to the allocated message structure or NULL if out of memory.
398 * @param target The target for the message.
399 * @param cbTotal Size of the payload in bytes.
400 * @param paSegs Pointer to the segment array making up the message payload.
401 * @param cSegs Number of segments in the array.
402 */
403DECLINLINE(PIPCMSG) IPCMsgNewSg(const nsID &target, size_t cbTotal, PCRTSGSEG paSegs, uint32_t cSegs)
404{
405 PIPCMSG pThis = IPCMsgAlloc(cbTotal);
406 if (RT_LIKELY(pThis))
407 ipcMsgInitWorker(pThis, target, cbTotal, paSegs, cSegs);
408
409 return pThis;
410}
411
412
413/**
414 * Initializes a given message frame, growing the data buffer if necessary.
415 *
416 * @param pThis The message to initialize.
417 * @param target the target for the message.
418 * @param cbTotal Size of the payload in bytes.
419 * @param paSegs Pointer to the segment array making up the message payload.
420 * @param cSegs Number of segments in the array.
421 */
422DECLINLINE(int) IPCMsgInitSg(PIPCMSG pThis, const nsID &target, size_t cbTotal, PCRTSGSEG paSegs, uint32_t cSegs)
423{
424 Assert(!pThis->fStack);
425
426 uint32_t cbMsg = sizeof(*pThis->pMsgHdr) + cbTotal;
427 if (pThis->cbBuf < cbMsg)
428 {
429 uint8_t *pbBufNew = (uint8_t *)RTMemRealloc(pThis->pbBuf, cbMsg);
430 if (!pbBufNew)
431 return VERR_NO_MEMORY;
432
433 pThis->pbBuf = pbBufNew;
434 pThis->cbBuf = cbMsg;
435 }
436
437 ipcMsgInitWorker(pThis, target, cbTotal, paSegs, cSegs);
438 return VINF_SUCCESS;
439}
440
441
442DECLINLINE(PIPCMSG) IPCMsgClone(PCIPCMSG pThis)
443{
444 size_t cbPayload = pThis->pMsgHdr->cbMsg - sizeof(*pThis->pMsgHdr);
445 PIPCMSG pClone = IPCMsgAlloc(cbPayload);
446 if (!pClone)
447 return NULL;
448
449 pClone->pMsgHdr = (PIPCMSGHDR)pClone->pbBuf;
450 *pClone->pMsgHdr = *pThis->pMsgHdr;
451 pClone->upUser = pThis->upUser;
452 memcpy(pClone->pMsgHdr + 1, pThis->pMsgHdr + 1, cbPayload);
453 return pClone;
454}
455
456
457DECLINLINE(int) IPCMsgCloneWithMsg(PCIPCMSG pThis, PIPCMSG pClone)
458{
459 RTSGSEG Seg;
460 Seg.pvSeg = (void *)IPCMsgGetPayload(pThis);
461 Seg.cbSeg = IPCMsgGetPayloadSize(pThis);
462
463 pClone->upUser = pThis->upUser;
464 return IPCMsgInitSg(pClone, *IPCMsgGetTarget(pThis),
465 Seg.cbSeg, &Seg, 1 /*cSeg*/);
466}
467
468
469/**
470 * Reads data for the given message from the given buffer.
471 *
472 * @returns IPRT status code.
473 * @param pThis The message to read into.
474 * @param pvData The data buffer holding message data.
475 * @param cbData Size of the data buffer in bytes.
476 * @param pcbRead Where to store the number of bytes read from the data buffer.
477 * @param pfDone Where to store the flag whether the message is complete and can be parsed after returning from this call.
478 */
479DECLINLINE(int) IPCMsgReadFrom(PIPCMSG pThis, const void *pvData, size_t cbData, size_t *pcbRead, bool *pfDone)
480{
481 Assert(!pThis->fStack);
482
483 size_t cbHdrRead = 0;
484 if (!pThis->pMsgHdr)
485 {
486 /* No full header received yet. */
487 Assert(pThis->cbBuf >= sizeof(*pThis->pMsgHdr));
488
489 cbHdrRead = RT_MIN(cbData, sizeof(*pThis->pMsgHdr) - pThis->offAppend);
490 memcpy(pThis->pbBuf + pThis->offAppend, pvData, cbHdrRead);
491
492 pThis->offAppend += cbHdrRead;
493 Assert(pThis->offAppend <= sizeof(*pThis->pMsgHdr));
494 if (pThis->offAppend == sizeof(*pThis->pMsgHdr))
495 {
496 /* Complete header received. */
497 pThis->pMsgHdr = (PIPCMSGHDR)pThis->pbBuf;
498
499 if (pThis->pMsgHdr->cbMsg > pThis->cbBuf)
500 {
501 Assert(pThis->pMsgHdr->cbMsg < 10 * _1M);
502
503 uint8_t *pbBufNew = (uint8_t *)RTMemRealloc(pThis->pbBuf, pThis->pMsgHdr->cbMsg);
504 if (!pbBufNew)
505 return VERR_NO_MEMORY;
506
507 pThis->pbBuf = pbBufNew;
508 pThis->pMsgHdr = (PIPCMSGHDR)pbBufNew;
509 pThis->cbBuf = pThis->pMsgHdr->cbMsg;
510 }
511
512 /* fall through below after adjusting the buffer pointers. */
513 pvData = (const uint8_t *)pvData + cbHdrRead;
514 cbData -= cbHdrRead;
515 }
516 else
517 {
518 *pfDone = false;
519 *pcbRead = cbHdrRead;
520 return VINF_SUCCESS;
521 }
522 }
523
524 Assert(pThis->pMsgHdr);
525 Assert(pThis->pMsgHdr->cbMsg <= pThis->cbBuf);
526
527 /* We know the size of the message because a full message header was received already. */
528 size_t cbLeft = pThis->pMsgHdr->cbMsg - pThis->offAppend;
529 cbData = RT_MIN(cbData, cbLeft);
530 Assert(pThis->offAppend + cbData <= pThis->cbBuf);
531
532 if (cbData == cbLeft)
533 *pfDone = true;
534 else
535 *pfDone = false;
536
537 memcpy(pThis->pbBuf + pThis->offAppend, pvData, cbData);
538 pThis->offAppend += cbData;
539 *pcbRead = cbData + cbHdrRead;
540 return VINF_SUCCESS;
541}
542
543#endif // !ipcMessageNew_h__
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use