1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*++ |
6 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | shmobject.hpp |
12 | |
13 | Abstract: |
14 | Shared memory based object |
15 | |
16 | |
17 | |
18 | --*/ |
19 | |
20 | #include "shmobject.hpp" |
21 | #include "pal/malloc.hpp" |
22 | #include "pal/cs.hpp" |
23 | #include "pal/dbgmsg.h" |
24 | |
25 | #include <stddef.h> |
26 | |
27 | SET_DEFAULT_DEBUG_CHANNEL(PAL); |
28 | |
29 | using namespace CorUnix; |
30 | |
31 | /*++ |
32 | Function: |
33 | CSharedMemoryObject::Initialize |
34 | |
35 | Performs possibly-failing initialization for a newly-constructed |
36 | object |
37 | |
38 | Parameters: |
39 | pthr -- thread data for calling thread |
40 | poa -- the object attributes (e.g., name) for the object |
41 | --*/ |
42 | |
43 | PAL_ERROR |
44 | CSharedMemoryObject::Initialize( |
45 | CPalThread *pthr, |
46 | CObjectAttributes *poa |
47 | ) |
48 | { |
49 | PAL_ERROR palError = NO_ERROR; |
50 | SHMObjData *psmod = NULL; |
51 | |
52 | _ASSERTE(NULL != pthr); |
53 | _ASSERTE(NULL != poa); |
54 | |
55 | ENTRY("CSharedMemoryObject::Initialize" |
56 | "(this = %p, pthr = %p, poa = %p)\n" , |
57 | this, |
58 | pthr, |
59 | poa |
60 | ); |
61 | |
62 | palError = CPalObjectBase::Initialize(pthr, poa); |
63 | if (NO_ERROR != palError) |
64 | { |
65 | goto InitializeExit; |
66 | } |
67 | |
68 | // |
69 | // If this is a named object it needs to go into the shared domain; |
70 | // otherwise it remains local |
71 | // |
72 | |
73 | if (0 != m_oa.sObjectName.GetStringLength()) |
74 | { |
75 | m_ObjectDomain = SharedObject; |
76 | |
77 | palError = AllocateSharedDataItems(&m_shmod, &psmod); |
78 | if (NO_ERROR != palError || NULL == psmod) |
79 | { |
80 | goto InitializeExit; |
81 | } |
82 | } |
83 | |
84 | if (0 != m_pot->GetSharedDataSize()) |
85 | { |
86 | if (SharedObject == m_ObjectDomain) |
87 | { |
88 | // |
89 | // Map the shared data into our address space |
90 | // |
91 | if (NULL == psmod) |
92 | { |
93 | ASSERT("psmod should not be NULL" ); |
94 | palError = ERROR_INTERNAL_ERROR; |
95 | goto InitializeExit; |
96 | } |
97 | |
98 | m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData); |
99 | if (NULL == m_pvSharedData) |
100 | { |
101 | ASSERT("Unable to map shared data area\n" ); |
102 | palError = ERROR_INTERNAL_ERROR; |
103 | goto InitializeExit; |
104 | } |
105 | } |
106 | else |
107 | { |
108 | // |
109 | // Initialize the local shared data lock. |
110 | // |
111 | |
112 | palError = m_sdlSharedData.Initialize(); |
113 | if (NO_ERROR != palError) |
114 | { |
115 | ERROR("Failure initializing m_sdlSharedData\n" ); |
116 | goto InitializeExit; |
117 | } |
118 | |
119 | // |
120 | // Allocate local memory to hold the shared data |
121 | // |
122 | |
123 | m_pvSharedData = InternalMalloc(m_pot->GetSharedDataSize()); |
124 | if (NULL == m_pvSharedData) |
125 | { |
126 | ERROR("Failure allocating m_pvSharedData (local copy)\n" ); |
127 | palError = ERROR_OUTOFMEMORY; |
128 | goto InitializeExit; |
129 | } |
130 | } |
131 | |
132 | ZeroMemory(m_pvSharedData, m_pot->GetSharedDataSize()); |
133 | } |
134 | |
135 | |
136 | InitializeExit: |
137 | |
138 | LOGEXIT("CSharedMemoryObject::Initalize returns %d\n" , palError); |
139 | |
140 | return palError; |
141 | } |
142 | |
143 | /*++ |
144 | Function: |
145 | CSharedMemoryObject::InitializeFromExistingSharedData |
146 | |
147 | Performs possibly-failing initialization for a newly-constructed |
148 | object that is to represent an existing object (i.e., importing |
149 | a shared object into this process) |
150 | |
151 | The shared memory lock must be held when calling this method |
152 | |
153 | Parameters: |
154 | pthr -- thread data for calling thread |
155 | poa -- the object attributes for the object |
156 | --*/ |
157 | |
158 | PAL_ERROR |
159 | CSharedMemoryObject::InitializeFromExistingSharedData( |
160 | CPalThread *pthr, |
161 | CObjectAttributes *poa |
162 | ) |
163 | { |
164 | PAL_ERROR palError = NO_ERROR; |
165 | SHMObjData *psmod = NULL; |
166 | |
167 | _ASSERTE(NULL != pthr); |
168 | _ASSERTE(NULL != poa); |
169 | |
170 | ENTRY("CSharedMemoryObject::InitializeFromExistingSharedData" |
171 | "(this = %p, pthr = %p, poa = %p)\n" , |
172 | this, |
173 | pthr, |
174 | poa |
175 | ); |
176 | |
177 | // |
178 | // This object is obviously shared... |
179 | // |
180 | |
181 | m_ObjectDomain = SharedObject; |
182 | |
183 | _ASSERTE(NULL != m_shmod); |
184 | |
185 | psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod); |
186 | if (NULL == psmod) |
187 | { |
188 | ASSERT("Unable to map shared object data\n" ); |
189 | palError = ERROR_INTERNAL_ERROR; |
190 | goto InitializeFromExistingSharedDataExit; |
191 | } |
192 | |
193 | // |
194 | // When we're being called on the duplicate handle path the passed |
195 | // in object attributes likely won't have an object name in it. |
196 | // If there is an object name in the shared data place that in the |
197 | // object attributs so that the constructed object has a local copy |
198 | // of the name |
199 | // |
200 | |
201 | if (0 == poa->sObjectName.GetStringLength() |
202 | && 0 != psmod->dwNameLength) |
203 | { |
204 | WCHAR *wsz; |
205 | |
206 | wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName); |
207 | if (NULL != wsz) |
208 | { |
209 | poa->sObjectName.SetStringWithLength(wsz, psmod->dwNameLength); |
210 | } |
211 | else |
212 | { |
213 | ASSERT("Unable to map object name\n" ); |
214 | palError = ERROR_INTERNAL_ERROR; |
215 | goto InitializeFromExistingSharedDataExit; |
216 | } |
217 | } |
218 | #if _DEBUG |
219 | else if (0 != psmod->dwNameLength) |
220 | { |
221 | WCHAR *wsz; |
222 | |
223 | // |
224 | // Verify that the names are consistent |
225 | // |
226 | |
227 | wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName); |
228 | _ASSERTE(NULL != wsz); |
229 | _ASSERTE(0 == PAL_wcscmp(wsz, poa->sObjectName.GetString())); |
230 | } |
231 | #endif // debug |
232 | |
233 | palError = CPalObjectBase::Initialize(pthr, poa); |
234 | if (NO_ERROR != palError) |
235 | { |
236 | goto InitializeFromExistingSharedDataExit; |
237 | } |
238 | |
239 | if (NULL != psmod->shmObjImmutableData) |
240 | { |
241 | VOID *pv = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData); |
242 | if (NULL != pv) |
243 | { |
244 | memcpy(m_pvImmutableData, pv, m_pot->GetImmutableDataSize()); |
245 | if (NULL != psmod->pCopyRoutine) |
246 | { |
247 | (*psmod->pCopyRoutine)(pv, m_pvImmutableData); |
248 | } |
249 | |
250 | m_pot->SetImmutableDataCopyRoutine(psmod->pCopyRoutine); |
251 | m_pot->SetImmutableDataCleanupRoutine(psmod->pCleanupRoutine); |
252 | } |
253 | else |
254 | { |
255 | ASSERT("Unable to map object immutable data\n" ); |
256 | palError = ERROR_INTERNAL_ERROR; |
257 | goto InitializeFromExistingSharedDataExit; |
258 | } |
259 | } |
260 | |
261 | if (NULL != psmod->shmObjSharedData) |
262 | { |
263 | m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData); |
264 | if (NULL == m_pvSharedData) |
265 | { |
266 | ASSERT("Unable to map object shared data\n" ); |
267 | palError = ERROR_INTERNAL_ERROR; |
268 | goto InitializeFromExistingSharedDataExit; |
269 | } |
270 | } |
271 | |
272 | if (NULL != m_pot->GetObjectInitRoutine()) |
273 | { |
274 | palError = (*m_pot->GetObjectInitRoutine())( |
275 | pthr, |
276 | m_pot, |
277 | m_pvImmutableData, |
278 | m_pvSharedData, |
279 | m_pvLocalData |
280 | ); |
281 | } |
282 | |
283 | InitializeFromExistingSharedDataExit: |
284 | |
285 | LOGEXIT("CSharedMemoryObject::InitalizeFromExistingSharedData returns %d\n" , palError); |
286 | |
287 | return palError; |
288 | } |
289 | |
290 | /*++ |
291 | Function: |
292 | CSharedMemoryObject::AllocatedSharedDataItems |
293 | |
294 | Allocates and initialiazes the shared memory structures necessary to make an |
295 | object available to other processes |
296 | |
297 | Parameters: |
298 | pshmObjData -- on success, receives the shared memory pointer for the |
299 | shared memory object data |
300 | ppsmod -- on success, receives the locally-mapped pointer for the shared |
301 | memory object data |
302 | --*/ |
303 | |
304 | PAL_ERROR |
305 | CSharedMemoryObject::AllocateSharedDataItems( |
306 | SHMPTR *pshmObjData, |
307 | SHMObjData **ppsmod |
308 | ) |
309 | { |
310 | PAL_ERROR palError = NO_ERROR; |
311 | SHMPTR shmod = NULL; |
312 | SHMObjData *psmod = NULL; |
313 | |
314 | _ASSERTE(NULL != pshmObjData); |
315 | _ASSERTE(NULL != ppsmod); |
316 | |
317 | ENTRY("CSharedMemoryObject::AllocateSharedDataItems" |
318 | "(this = %p, pshmObjData = %p, ppsmod = %p)\n" , |
319 | this, |
320 | pshmObjData, |
321 | ppsmod |
322 | ); |
323 | |
324 | // |
325 | // We're about to make a number of shared memory allocations, |
326 | // so grab the lock for the entirety of the routine. |
327 | // |
328 | |
329 | SHMLock(); |
330 | |
331 | shmod = malloc(sizeof(SHMObjData)); |
332 | if (NULL == shmod) |
333 | { |
334 | ERROR("Unable to allocate m_shmod for new object\n" ); |
335 | palError = ERROR_OUTOFMEMORY; |
336 | goto AllocateSharedDataItemsExit; |
337 | } |
338 | |
339 | psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmod); |
340 | _ASSERTE(NULL != psmod); |
341 | |
342 | ZeroMemory(psmod, sizeof(*psmod)); |
343 | |
344 | psmod->eTypeId = m_pot->GetId(); |
345 | psmod->lProcessRefCount = 1; |
346 | |
347 | if (0 != m_oa.sObjectName.GetStringLength()) |
348 | { |
349 | LPCWSTR str = m_oa.sObjectName.GetString(); |
350 | _ASSERTE(str); |
351 | |
352 | psmod->dwNameLength = m_oa.sObjectName.GetStringLength(); |
353 | |
354 | UINT length = (PAL_wcslen(str) + 1) * sizeof(WCHAR); |
355 | psmod->shmObjName = malloc(length); |
356 | |
357 | if (psmod->shmObjName != 0) |
358 | { |
359 | memcpy(psmod->shmObjName, str, length); |
360 | } |
361 | else |
362 | { |
363 | ERROR("Unable to allocate psmod->shmObjName for new object\n" ); |
364 | palError = ERROR_OUTOFMEMORY; |
365 | goto AllocateSharedDataItemsExit; |
366 | } |
367 | } |
368 | |
369 | if (0 != m_pot->GetImmutableDataSize()) |
370 | { |
371 | // |
372 | // The shared copy of the object's immutable data will be initialized |
373 | // by CSharedMemoryObjectManager::RegisterObject or PromoteSharedData |
374 | // |
375 | |
376 | psmod->shmObjImmutableData = malloc(m_pot->GetImmutableDataSize()); |
377 | if (NULL == psmod->shmObjImmutableData) |
378 | { |
379 | ERROR("Unable to allocate psmod->shmObjImmutableData for new object\n" ); |
380 | palError = ERROR_OUTOFMEMORY; |
381 | goto AllocateSharedDataItemsExit; |
382 | } |
383 | } |
384 | |
385 | if (0 != m_pot->GetSharedDataSize()) |
386 | { |
387 | psmod->shmObjSharedData = malloc(m_pot->GetSharedDataSize()); |
388 | if (NULL == psmod->shmObjSharedData) |
389 | { |
390 | ERROR("Unable to allocate psmod->shmObjSharedData for new object\n" ); |
391 | palError = ERROR_OUTOFMEMORY; |
392 | goto AllocateSharedDataItemsExit; |
393 | } |
394 | } |
395 | |
396 | *pshmObjData = shmod; |
397 | *ppsmod = psmod; |
398 | |
399 | AllocateSharedDataItemsExit: |
400 | |
401 | if (NO_ERROR != palError && NULL != shmod) |
402 | { |
403 | FreeSharedDataAreas(shmod); |
404 | } |
405 | |
406 | SHMRelease(); |
407 | |
408 | LOGEXIT("CSharedMemoryObject::AllocateSharedDataItems returns %d\n" , palError); |
409 | |
410 | return palError; |
411 | } |
412 | |
413 | /*++ |
414 | Function: |
415 | CSharedMemoryObject::FreeSharedDataItems |
416 | |
417 | Frees the shared memory structures referenced by the provided shared |
418 | memory pointer |
419 | |
420 | Parameters: |
421 | shmObjData -- shared memory pointer to the structures to free |
422 | --*/ |
423 | |
424 | // static |
425 | void |
426 | CSharedMemoryObject::FreeSharedDataAreas( |
427 | SHMPTR shmObjData |
428 | ) |
429 | { |
430 | SHMObjData *psmod; |
431 | |
432 | _ASSERTE(NULL != shmObjData); |
433 | |
434 | ENTRY("CSharedMemoryObject::FreeSharedDataAreas" |
435 | "(shmObjData = %p)\n" , |
436 | shmObjData |
437 | ); |
438 | |
439 | SHMLock(); |
440 | |
441 | psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjData); |
442 | _ASSERTE(NULL != psmod); |
443 | |
444 | if (NULL != psmod->shmObjImmutableData) |
445 | { |
446 | if (NULL != psmod->pCleanupRoutine) |
447 | { |
448 | (*psmod->pCleanupRoutine)(psmod->shmObjImmutableData); |
449 | } |
450 | free(psmod->shmObjImmutableData); |
451 | } |
452 | |
453 | if (NULL != psmod->shmObjSharedData) |
454 | { |
455 | free(psmod->shmObjSharedData); |
456 | } |
457 | |
458 | if (NULL != psmod->shmObjName) |
459 | { |
460 | free(psmod->shmObjName); |
461 | } |
462 | |
463 | free(shmObjData); |
464 | |
465 | SHMRelease(); |
466 | |
467 | LOGEXIT("CSharedMemoryObject::FreeSharedDataAreas\n" ); |
468 | } |
469 | |
470 | /*++ |
471 | Function: |
472 | CSharedMemoryObject::CleanupForProcessShutdown |
473 | |
474 | Cleanup routine called by the object manager when shutting down |
475 | |
476 | Parameters: |
477 | pthr -- thread data for the calling thread |
478 | --*/ |
479 | |
480 | void |
481 | CSharedMemoryObject::CleanupForProcessShutdown( |
482 | CPalThread *pthr |
483 | ) |
484 | { |
485 | bool fCleanupSharedState; |
486 | |
487 | _ASSERTE(NULL != pthr); |
488 | |
489 | ENTRY("CSharedMemoryObject::CleanupForProcessShutdown" |
490 | "(this = %p, pthr = %p)\n" , |
491 | this, |
492 | pthr |
493 | ); |
494 | |
495 | fCleanupSharedState = DereferenceSharedData(); |
496 | |
497 | if (NULL != m_pot->GetObjectCleanupRoutine()) |
498 | { |
499 | (*m_pot->GetObjectCleanupRoutine())( |
500 | pthr, |
501 | static_cast<IPalObject*>(this), |
502 | TRUE, |
503 | fCleanupSharedState |
504 | ); |
505 | } |
506 | |
507 | if (NULL != m_pot->GetImmutableDataCleanupRoutine()) |
508 | { |
509 | (*m_pot->GetImmutableDataCleanupRoutine())(m_pvImmutableData); |
510 | } |
511 | |
512 | if (NULL != m_pot->GetProcessLocalDataCleanupRoutine()) |
513 | { |
514 | (*m_pot->GetProcessLocalDataCleanupRoutine())(pthr, static_cast<IPalObject*>(this)); |
515 | } |
516 | |
517 | // |
518 | // We need to do two things with the calling thread data here: |
519 | // 1) store it in m_pthrCleanup so it is available to the destructors |
520 | // 2) Add a reference to it before starting any cleanup, and release |
521 | // that reference afterwords. |
522 | // |
523 | // Step 2 is necessary when we're cleaning up the thread object that |
524 | // represents the calling thread -- it ensures that the thread data |
525 | // is available throughout the entire cleanup process. |
526 | // |
527 | |
528 | m_pthrCleanup = pthr; |
529 | pthr->AddThreadReference(); |
530 | |
531 | InternalDelete(this); |
532 | |
533 | pthr->ReleaseThreadReference(); |
534 | |
535 | LOGEXIT("CSharedMemoryObject::CleanupForProcessShutdown\n" ); |
536 | } |
537 | |
538 | /*++ |
539 | Function: |
540 | CSharedMemoryObject::AcquiteObjectDestructionLock |
541 | |
542 | Acquires the lock that must be held when decrementing the object's |
543 | reference count (and, if the count drops to 0, while removing the |
544 | object from the object manager's lists). |
545 | |
546 | Parameters: |
547 | pthr -- thread data for the calling thread |
548 | --*/ |
549 | |
550 | void |
551 | CSharedMemoryObject::AcquireObjectDestructionLock( |
552 | CPalThread *pthr |
553 | ) |
554 | { |
555 | _ASSERTE(NULL != pthr); |
556 | |
557 | ENTRY("CSharedMemoryObject::AcquireObjectDestructionLock" |
558 | "(this = %p, pthr = $p)\n" , |
559 | this, |
560 | pthr |
561 | ); |
562 | |
563 | InternalEnterCriticalSection(pthr, m_pcsObjListLock); |
564 | |
565 | LOGEXIT("CSharedMemoryObject::AcquireObjectDestructionLock\n" ); |
566 | } |
567 | |
568 | /*++ |
569 | Function: |
570 | CSharedMemoryObject::ReleaseObjectDestructionLock |
571 | |
572 | Releases the lock acquired by AcquireObjectDestructionLock |
573 | |
574 | Parameters: |
575 | pthr -- thread data for the calling thread |
576 | fDestructionPending -- if TRUE, the reference count for this |
577 | object has dropped to 0; the object will be destroyed after |
578 | this routine returns |
579 | --*/ |
580 | |
581 | bool |
582 | CSharedMemoryObject::ReleaseObjectDestructionLock( |
583 | CPalThread *pthr, |
584 | bool fDestructionPending |
585 | ) |
586 | { |
587 | bool fCleanupSharedState = FALSE; |
588 | |
589 | _ASSERTE(NULL != pthr); |
590 | |
591 | ENTRY("CSharedMemoryObject::ReleaseObjectDestructionLock" |
592 | "(this = %p, pthr = %p, fDestructionPending = %d\n" , |
593 | this, |
594 | pthr, |
595 | fDestructionPending |
596 | ); |
597 | |
598 | if (fDestructionPending) |
599 | { |
600 | RemoveEntryList(&m_le); |
601 | fCleanupSharedState = DereferenceSharedData(); |
602 | } |
603 | |
604 | InternalLeaveCriticalSection(pthr, m_pcsObjListLock); |
605 | |
606 | LOGEXIT("CSharedMemoryObject::ReleaseObjectDestructionLock returns %d\n" , |
607 | fCleanupSharedState |
608 | ); |
609 | |
610 | return fCleanupSharedState; |
611 | } |
612 | |
613 | /*++ |
614 | Function: |
615 | CSharedMemoryObject::DereferenceSharedData |
616 | |
617 | Called to decrement the global refcount (i.e., the count of |
618 | the number of processes that have reference to the object) when |
619 | the local reference to the object is being destroyed. |
620 | |
621 | Return value: |
622 | Returns TRUE if this process needs to clean up the object's shared |
623 | data (i.e., the global refcount has dropped to 0, or the object |
624 | is in the local domain) |
625 | --*/ |
626 | |
627 | bool |
628 | CSharedMemoryObject::DereferenceSharedData() |
629 | { |
630 | LONG fSharedDataAlreadDereferenced; |
631 | |
632 | ENTRY("CSharedMemoryObject::DereferenceSharedData(this = %p)\n" , this); |
633 | |
634 | fSharedDataAlreadDereferenced = InterlockedExchange( |
635 | &m_fSharedDataDereferenced, |
636 | TRUE |
637 | ); |
638 | |
639 | if (!fSharedDataAlreadDereferenced) |
640 | { |
641 | if (NULL != m_shmod) |
642 | { |
643 | SHMObjData *psmod; |
644 | |
645 | SHMLock(); |
646 | |
647 | psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod); |
648 | _ASSERTE(NULL != psmod); |
649 | |
650 | psmod->lProcessRefCount -= 1; |
651 | if (0 == psmod->lProcessRefCount) |
652 | { |
653 | // |
654 | // No other process is using this object, so remove |
655 | // it from the shared memory named object list (if it |
656 | // had been added to it). The final cleanup will happen |
657 | // in the object's destructor |
658 | // |
659 | |
660 | m_fDeleteSharedData = TRUE; |
661 | |
662 | if (psmod->fAddedToList) |
663 | { |
664 | // |
665 | // This object better have a name... |
666 | // |
667 | |
668 | _ASSERTE(0 != psmod->dwNameLength); |
669 | |
670 | if (NULL != psmod->shmPrevObj) |
671 | { |
672 | SHMObjData *psmodPrevious = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmPrevObj); |
673 | _ASSERTE(NULL != psmodPrevious); |
674 | |
675 | psmodPrevious->shmNextObj = psmod->shmNextObj; |
676 | } |
677 | else |
678 | { |
679 | // |
680 | // This object is the head of the shared memory named object |
681 | // list -- reset that pointer now |
682 | // |
683 | |
684 | if (!SHMSetInfo(SIID_NAMED_OBJECTS, psmod->shmNextObj)) |
685 | { |
686 | ASSERT("Failed to set shared named object list head" ); |
687 | } |
688 | } |
689 | |
690 | if (NULL != psmod->shmNextObj) |
691 | { |
692 | SHMObjData *psmodNext = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmNextObj); |
693 | _ASSERTE(NULL != psmodNext); |
694 | |
695 | psmodNext->shmPrevObj = psmod->shmPrevObj; |
696 | } |
697 | } |
698 | #if _DEBUG |
699 | else |
700 | { |
701 | _ASSERTE(NULL == psmod->shmPrevObj); |
702 | _ASSERTE(NULL == psmod->shmNextObj); |
703 | } |
704 | #endif |
705 | } |
706 | |
707 | SHMRelease(); |
708 | } |
709 | else if (ProcessLocalObject == m_ObjectDomain) |
710 | { |
711 | // |
712 | // If the object is local the shared data needs to be |
713 | // deleted by definition |
714 | // |
715 | |
716 | m_fDeleteSharedData = TRUE; |
717 | } |
718 | } |
719 | else |
720 | { |
721 | ASSERT("Multiple calls to DereferenceSharedData\n" ); |
722 | } |
723 | |
724 | LOGEXIT("CSharedMemoryObject::DereferenceSharedData returns %d\n" , |
725 | m_fDeleteSharedData |
726 | ); |
727 | |
728 | return m_fDeleteSharedData; |
729 | } |
730 | |
731 | /*++ |
732 | Function: |
733 | CSharedMemoryObject::~CSharedMemoryObject |
734 | |
735 | Destructor; should only be called from ReleaseReference |
736 | --*/ |
737 | |
738 | CSharedMemoryObject::~CSharedMemoryObject() |
739 | { |
740 | ENTRY("CSharedMemoryObject::~CSharedMemoryObject(this = %p)\n" , this); |
741 | |
742 | if (!m_fSharedDataDereferenced) |
743 | { |
744 | ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n" ); |
745 | DereferenceSharedData(); |
746 | } |
747 | |
748 | if (NULL != m_pvSharedData && ProcessLocalObject == m_ObjectDomain) |
749 | { |
750 | free(m_pvSharedData); |
751 | } |
752 | else if (NULL != m_shmod && m_fDeleteSharedData) |
753 | { |
754 | FreeSharedDataAreas(m_shmod); |
755 | } |
756 | |
757 | LOGEXIT("CSharedMemoryObject::~CSharedMemoryObject\n" ); |
758 | } |
759 | |
760 | // |
761 | // C++ standard, 18.1.5 - offsetof requires a POD (plain old data) struct or |
762 | // union. Since offsetof is a macro, gcc doesn't actually check for improper |
763 | // use of offsetof, it keys off of the -> from NULL (which is also invalid for |
764 | // non-POD types by 18.1.5) |
765 | // |
766 | // As we have numerous examples of this behavior in our codebase, |
767 | // making an offsetof which doesn't use 0. |
768 | // |
769 | // PAL_safe_offsetof is a version of offsetof that protects against an |
770 | // overridden operator& |
771 | // |
772 | |
773 | #define PAL_safe_offsetof(s,m) ((size_t)((ptrdiff_t)&(char&)(((s *)64)->m))-64) |
774 | |
775 | /*++ |
776 | Function: |
777 | CSharedMemoryObject::GetObjectFromListLink |
778 | |
779 | Given a list link returns the object that contains it. Since m_le is |
780 | protected the caller cannot perform this computation directly |
781 | |
782 | Parameters: |
783 | ple -- the list entry to obtain the object for |
784 | --*/ |
785 | |
786 | // static |
787 | CSharedMemoryObject* |
788 | CSharedMemoryObject::GetObjectFromListLink(PLIST_ENTRY ple) |
789 | { |
790 | CSharedMemoryObject *pshmo; |
791 | |
792 | _ASSERTE(NULL != ple); |
793 | |
794 | ENTRY("CSharedMemoryObject::GetObjectFromListLink(ple = %p)\n" , ple); |
795 | |
796 | // |
797 | // Ideally we'd use CONTAINING_RECORD here, but it uses offsetof (see above |
798 | // comment |
799 | // |
800 | |
801 | pshmo = reinterpret_cast<CSharedMemoryObject*>( |
802 | reinterpret_cast<size_t>(ple) - PAL_safe_offsetof(CSharedMemoryObject, m_le) |
803 | ); |
804 | |
805 | _ASSERTE(ple == &pshmo->m_le); |
806 | |
807 | LOGEXIT("CSharedMemoryObject::GetObjectFromListLink returns %p\n" , pshmo); |
808 | |
809 | return pshmo; |
810 | } |
811 | |
812 | /*++ |
813 | Function: |
814 | CSharedMemoryObject::GetSharedData |
815 | |
816 | Provides the caller access to the object's shared data (if any) |
817 | |
818 | Parameters: |
819 | pthr -- thread data for calling thread |
820 | eLockRequest -- specifies if the caller desires a read lock or a |
821 | write lock on the data (currently ignored) |
822 | ppDataLock -- on success, receives a pointer to the data lock instance |
823 | for the shared data |
824 | ppvProcssSharedData -- on success, receives a pointer to the shared data |
825 | --*/ |
826 | |
827 | PAL_ERROR |
828 | CSharedMemoryObject::GetSharedData( |
829 | CPalThread *pthr, |
830 | LockType eLockRequest, |
831 | IDataLock **ppDataLock, // OUT |
832 | void **ppvSharedData // OUT |
833 | ) |
834 | { |
835 | IDataLock *pDataLock; |
836 | |
837 | _ASSERTE(NULL != pthr); |
838 | _ASSERTE(ReadLock == eLockRequest || WriteLock == eLockRequest); |
839 | _ASSERTE(NULL != ppDataLock); |
840 | _ASSERTE(NULL != ppvSharedData); |
841 | |
842 | ENTRY("CSharedMemoryObject::GetSharedData" |
843 | "(this = %p, pthr = %p, eLockRequest = %d, ppDataLock = %p," |
844 | " ppvSharedData = %p)\n" , |
845 | this, |
846 | pthr, |
847 | eLockRequest, |
848 | ppDataLock, |
849 | ppvSharedData |
850 | ); |
851 | |
852 | _ASSERTE(0 < m_pot->GetSharedDataSize()); |
853 | |
854 | if (ProcessLocalObject == m_ObjectDomain) |
855 | { |
856 | // |
857 | // We need to grab the local shared data lock and re-check |
858 | // the object's domain, as there's a chance the object might |
859 | // have been promoted after we made the above check but before |
860 | // we grabbed the lock |
861 | // |
862 | |
863 | m_sdlSharedData.AcquireLock(pthr, &pDataLock); |
864 | |
865 | if (SharedObject == m_ObjectDomain) |
866 | { |
867 | pDataLock->ReleaseLock(pthr, FALSE); |
868 | m_ssmlSharedData.AcquireLock(pthr, &pDataLock); |
869 | } |
870 | } |
871 | else |
872 | { |
873 | // |
874 | // A shared object can never transition back to local, |
875 | // so there's no need to recheck the domain on this path |
876 | // |
877 | |
878 | m_ssmlSharedData.AcquireLock(pthr, &pDataLock); |
879 | } |
880 | |
881 | *ppDataLock = pDataLock; |
882 | *ppvSharedData = m_pvSharedData; |
883 | |
884 | LOGEXIT("CSharedMemoryObject::GetSharedData returns %d\n" , NO_ERROR); |
885 | |
886 | return NO_ERROR; |
887 | } |
888 | |
889 | /*++ |
890 | Function: |
891 | CSharedMemoryObject::GetSynchStateController |
892 | |
893 | Obtain a synchronization state controller for this object. Should |
894 | never be called. |
895 | |
896 | Parameters: |
897 | pthr -- thread data for calling thread |
898 | ppStateController -- on success, receives a pointer to the state controller |
899 | instance |
900 | --*/ |
901 | |
902 | PAL_ERROR |
903 | CSharedMemoryObject::GetSynchStateController( |
904 | CPalThread *pthr, |
905 | ISynchStateController **ppStateController // OUT |
906 | ) |
907 | { |
908 | _ASSERTE(NULL != pthr); |
909 | _ASSERTE(NULL != ppStateController); |
910 | |
911 | // |
912 | // This is not a waitable object! |
913 | // |
914 | |
915 | ASSERT("Attempt to obtain a synch state controller on a non-waitable object\n" ); |
916 | return ERROR_INVALID_HANDLE; |
917 | } |
918 | |
919 | /*++ |
920 | Function: |
921 | CSharedMemoryObject::GetSynchWaitController |
922 | |
923 | Obtain a synchronization wait controller for this object. Should |
924 | never be called. |
925 | |
926 | Parameters: |
927 | pthr -- thread data for calling thread |
928 | ppWaitController -- on success, receives a pointer to the wait controller |
929 | instance |
930 | --*/ |
931 | |
932 | PAL_ERROR |
933 | CSharedMemoryObject::GetSynchWaitController( |
934 | CPalThread *pthr, |
935 | ISynchWaitController **ppWaitController // OUT |
936 | ) |
937 | { |
938 | _ASSERTE(NULL != pthr); |
939 | _ASSERTE(NULL != ppWaitController); |
940 | |
941 | // |
942 | // This is not a waitable object!!! |
943 | // |
944 | |
945 | ASSERT("Attempt to obtain a synch wait controller on a non-waitable object\n" ); |
946 | return ERROR_INVALID_HANDLE; |
947 | } |
948 | |
949 | /*++ |
950 | Function: |
951 | CSharedMemoryObject::GetObjectDomain |
952 | |
953 | Returns the object's domain (local or shared) |
954 | |
955 | --*/ |
956 | |
957 | ObjectDomain |
958 | CSharedMemoryObject::GetObjectDomain( |
959 | void |
960 | ) |
961 | { |
962 | TRACE("CSharedMemoryObject::GetObjectDomain(this = %p)\n" , this); |
963 | LOGEXIT("CSharedMemoryObject::GetObjectDomain returns %d\n" , m_ObjectDomain); |
964 | |
965 | return m_ObjectDomain; |
966 | } |
967 | |
968 | /*++ |
969 | Function: |
970 | CSharedMemoryObject::GetObjectSynchData |
971 | |
972 | Obtain the synchronization data for this object. Should |
973 | never be called. |
974 | |
975 | Parameters: |
976 | ppvSynchData -- on success, receives a pointer to the object's synch data |
977 | --*/ |
978 | |
979 | PAL_ERROR |
980 | CSharedMemoryObject::GetObjectSynchData( |
981 | VOID **ppvSynchData // OUT |
982 | ) |
983 | { |
984 | _ASSERTE(NULL != ppvSynchData); |
985 | |
986 | // |
987 | // This is not a waitable object!!! |
988 | // |
989 | |
990 | ASSERT("Attempt to obtain a synch data for a non-waitable object\n" ); |
991 | return ERROR_INVALID_HANDLE; |
992 | } |
993 | |
994 | /*++ |
995 | Function: |
996 | CSharedMemoryWaitableObject::Initialize |
997 | |
998 | Performs possibly-failing initialization for a newly-constructed |
999 | object |
1000 | |
1001 | Parameters: |
1002 | pthr -- thread data for calling thread |
1003 | poa -- the object attributes (e.g., name) for the object |
1004 | --*/ |
1005 | |
1006 | PAL_ERROR |
1007 | CSharedMemoryWaitableObject::Initialize( |
1008 | CPalThread *pthr, |
1009 | CObjectAttributes *poa |
1010 | ) |
1011 | { |
1012 | PAL_ERROR palError = NO_ERROR; |
1013 | |
1014 | _ASSERTE(NULL != pthr); |
1015 | _ASSERTE(NULL != poa); |
1016 | |
1017 | ENTRY("CSharedMemoryWaitableObject::Initialize" |
1018 | "(this = %p, pthr = %p, poa = %p)\n" , |
1019 | this, |
1020 | pthr, |
1021 | poa |
1022 | ); |
1023 | |
1024 | palError = CSharedMemoryObject::Initialize(pthr, poa); |
1025 | if (NO_ERROR != palError) |
1026 | { |
1027 | goto InitializeExit; |
1028 | } |
1029 | |
1030 | // |
1031 | // Sanity check the passed in object type |
1032 | // |
1033 | |
1034 | _ASSERTE(CObjectType::WaitableObject == m_pot->GetSynchronizationSupport()); |
1035 | |
1036 | palError = g_pSynchronizationManager->AllocateObjectSynchData( |
1037 | m_pot, |
1038 | m_ObjectDomain, |
1039 | &m_pvSynchData |
1040 | ); |
1041 | |
1042 | if (NO_ERROR == palError && SharedObject == m_ObjectDomain) |
1043 | { |
1044 | SHMObjData *pshmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod); |
1045 | _ASSERTE(NULL != pshmod); |
1046 | |
1047 | pshmod->pvSynchData = m_pvSynchData; |
1048 | } |
1049 | |
1050 | InitializeExit: |
1051 | |
1052 | LOGEXIT("CSharedMemoryWaitableObject::Initialize returns %d\n" , palError); |
1053 | |
1054 | return palError; |
1055 | } |
1056 | |
1057 | /*++ |
1058 | Function: |
1059 | CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject |
1060 | |
1061 | Destructor; should only be called from ReleaseReference |
1062 | --*/ |
1063 | |
1064 | CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject() |
1065 | { |
1066 | ENTRY("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject" |
1067 | "(this = %p)\n" , |
1068 | this |
1069 | ); |
1070 | |
1071 | if (!m_fSharedDataDereferenced) |
1072 | { |
1073 | ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n" ); |
1074 | DereferenceSharedData(); |
1075 | } |
1076 | |
1077 | if (NULL != m_pvSynchData && m_fDeleteSharedData) |
1078 | { |
1079 | g_pSynchronizationManager->FreeObjectSynchData( |
1080 | m_pot, |
1081 | m_ObjectDomain, |
1082 | m_pvSynchData |
1083 | ); |
1084 | } |
1085 | |
1086 | LOGEXIT("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject\n" ); |
1087 | } |
1088 | |
1089 | /*++ |
1090 | Function: |
1091 | CSharedMemoryWaitableObject::GetSynchStateController |
1092 | |
1093 | Obtain a synchronization state controller for this object. |
1094 | |
1095 | Parameters: |
1096 | pthr -- thread data for calling thread |
1097 | ppStateController -- on success, receives a pointer to the state controller |
1098 | instance |
1099 | --*/ |
1100 | |
1101 | PAL_ERROR |
1102 | CSharedMemoryWaitableObject::GetSynchStateController( |
1103 | CPalThread *pthr, // IN, OPTIONAL |
1104 | ISynchStateController **ppStateController // OUT |
1105 | ) |
1106 | { |
1107 | PAL_ERROR palError = NO_ERROR; |
1108 | |
1109 | _ASSERTE(NULL != pthr); |
1110 | _ASSERTE(NULL != ppStateController); |
1111 | |
1112 | ENTRY("CSharedMemoryWaitableObject::GetSynchStateController" |
1113 | "(this = %p, pthr = %p, ppStateController = %p" , |
1114 | this, |
1115 | pthr, |
1116 | ppStateController |
1117 | ); |
1118 | |
1119 | // |
1120 | // We need to grab the local synch lock before creating the controller |
1121 | // (otherwise we could get promoted after passing in our parameters) |
1122 | // |
1123 | |
1124 | g_pSynchronizationManager->AcquireProcessLock(pthr); |
1125 | |
1126 | palError = g_pSynchronizationManager->CreateSynchStateController( |
1127 | pthr, |
1128 | m_pot, |
1129 | m_pvSynchData, |
1130 | m_ObjectDomain, |
1131 | ppStateController |
1132 | ); |
1133 | |
1134 | g_pSynchronizationManager->ReleaseProcessLock(pthr); |
1135 | |
1136 | LOGEXIT("CSharedMemoryWaitableObject::GetSynchStateController returns %d\n" , |
1137 | palError |
1138 | ); |
1139 | |
1140 | return palError; |
1141 | } |
1142 | |
1143 | /*++ |
1144 | Function: |
1145 | CSharedMemoryWaitableObject::GetSynchWaitController |
1146 | |
1147 | Obtain a synchronization wait controller for this object. |
1148 | |
1149 | Parameters: |
1150 | pthr -- thread data for calling thread |
1151 | ppWaitController -- on success, receives a pointer to the wait controller |
1152 | instance |
1153 | --*/ |
1154 | |
1155 | PAL_ERROR |
1156 | CSharedMemoryWaitableObject::GetSynchWaitController( |
1157 | CPalThread *pthr, // IN, OPTIONAL |
1158 | ISynchWaitController **ppWaitController // OUT |
1159 | ) |
1160 | { |
1161 | PAL_ERROR palError = NO_ERROR; |
1162 | |
1163 | _ASSERTE(NULL != pthr); |
1164 | _ASSERTE(NULL != ppWaitController); |
1165 | |
1166 | ENTRY("CSharedMemoryWaitableObject::GetSynchWaitController" |
1167 | "(this = %p, pthr = %p, ppWaitController = %p" , |
1168 | this, |
1169 | pthr, |
1170 | ppWaitController |
1171 | ); |
1172 | |
1173 | // |
1174 | // We need to grab the local synch lock before creating the controller |
1175 | // (otherwise we could get promoted after passing in our parameters) |
1176 | // |
1177 | |
1178 | g_pSynchronizationManager->AcquireProcessLock(pthr); |
1179 | |
1180 | palError = g_pSynchronizationManager->CreateSynchWaitController( |
1181 | pthr, |
1182 | m_pot, |
1183 | m_pvSynchData, |
1184 | m_ObjectDomain, |
1185 | ppWaitController |
1186 | ); |
1187 | |
1188 | g_pSynchronizationManager->ReleaseProcessLock(pthr); |
1189 | |
1190 | LOGEXIT("CSharedMemoryWaitableObject::GetSynchWaitController returns %d\n" , |
1191 | palError |
1192 | ); |
1193 | |
1194 | return palError; |
1195 | } |
1196 | |
1197 | /*++ |
1198 | Function: |
1199 | CSharedMemoryWaitableObject::GetObjectSynchData |
1200 | |
1201 | Obtain the synchronization data for this object. This method should only |
1202 | be called by the synchronization manager |
1203 | |
1204 | Parameters: |
1205 | ppvSynchData -- on success, receives a pointer to the object's synch data |
1206 | --*/ |
1207 | |
1208 | PAL_ERROR |
1209 | CSharedMemoryWaitableObject::GetObjectSynchData( |
1210 | VOID **ppvSynchData // OUT |
1211 | ) |
1212 | { |
1213 | _ASSERTE(NULL != ppvSynchData); |
1214 | |
1215 | ENTRY("CSharedMemoryWaitableObject::GetObjectSynchData" |
1216 | "(this = %p, ppvSynchData = %p)\n" , |
1217 | this, |
1218 | ppvSynchData |
1219 | ); |
1220 | |
1221 | *ppvSynchData = m_pvSynchData; |
1222 | |
1223 | LOGEXIT("CSharedMemoryWaitableObject::GetObjectSynchData returns %d\n" , |
1224 | NO_ERROR |
1225 | ); |
1226 | |
1227 | return NO_ERROR; |
1228 | } |
1229 | |
1230 | |