Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1505 lines
37 KiB

  1. /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. Copyright (c) 1993-2000 Microsoft Corporation
  3. Module Name:
  4. rpcssm.cxx
  5. Abstract:
  6. RpcS* memory package routines are implemented here.
  7. These corespond to DCE rpc_ss_* and rpc_sm_* routines.
  8. Author:
  9. Michael Montague (mikemon) 12-Apr-1993
  10. Revision History:
  11. Ryszardk Nov 30, 1993 added rpc_sm_* package,
  12. rewrote rpc_ss_* package
  13. -------------------------------------------------------------------*/
  14. #include <assert.h>
  15. #include <ndrp.h> // rpcndr.h and NDR_ASSERT
  16. void NdrRpcDeleteAllocationContext();
  17. void ForceNdrCleanupSegIntoMemory();
  18. #include "util.hxx"
  19. #include "rpcssm.hxx"
  20. // =======================================================================
  21. // RpcSs Package
  22. // =======================================================================
  23. // This structure is being initialized with (plain) malloc and free
  24. // by an assignement in NdrClientInitializeNew.
  25. // It is used only by the client side.
  26. // We need a pointer, too, because of a C compiler problem on Dos.
  27. MALLOC_FREE_STRUCT RpcSsDefaults = { 0, 0 };
  28. MALLOC_FREE_STRUCT * pRpcSsDefaults = &RpcSsDefaults;
  29. EXTERN_C void NdrpSetRpcSsDefaults(RPC_CLIENT_ALLOC *pfnAlloc,
  30. RPC_CLIENT_FREE *pfnFree)
  31. {
  32. pRpcSsDefaults->pfnAllocate = pfnAlloc;
  33. pRpcSsDefaults->pfnFree = pfnFree;
  34. }
  35. // These are default allocator and deallocator used by the client side
  36. // when the memory package hasn't been enabled. They map to C-runtime
  37. // malloc and free.
  38. // When the client side executes in an enabled environment, the client
  39. // doesn't use these two routines; instead, it uses the same default
  40. // allocator that the server does (NdrRpcSsDefaultAllocate/Free).
  41. // Those map into I_RpcAllocate/I_RpcFree, and those in turn map into
  42. // system Rtl* routines.
  43. static void * __RPC_API
  44. DefaultAllocate (
  45. IN size_t Size
  46. )
  47. {
  48. if ( RpcSsDefaults.pfnAllocate == NULL )
  49. RpcRaiseException( RPC_X_WRONG_STUB_VERSION );
  50. return( RpcSsDefaults.pfnAllocate( Size ) ) ;
  51. }
  52. static void __RPC_API
  53. DefaultFree (
  54. IN void * Ptr
  55. )
  56. {
  57. if ( RpcSsDefaults.pfnFree == NULL )
  58. RpcRaiseException( RPC_X_WRONG_STUB_VERSION );
  59. RpcSsDefaults.pfnFree( Ptr );
  60. }
  61. // -----------------------------------------------------------------
  62. PALLOCATION_CONTEXT
  63. GetCreateAllocationContext (
  64. )
  65. /*++
  66. Return Value:
  67. The allocation information for this thread is returned. If there is
  68. no allocation information context for this thread, one gets allocated.
  69. If there is insufficient memory, an exception is raised.
  70. Exceptions:
  71. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  72. will be raised.
  73. Notes.
  74. pBlocDescr == 0 means that the memory package is disabled.
  75. --*/
  76. {
  77. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();;
  78. if ( AllocationContext == 0 )
  79. {
  80. if ( NdrpPerformRpcInitialization() == RPC_S_OK )
  81. {
  82. AllocationContext = (PALLOCATION_CONTEXT)
  83. I_RpcAllocate( sizeof(ALLOCATION_CONTEXT) );
  84. }
  85. if ( AllocationContext == 0 )
  86. {
  87. RpcRaiseException(RPC_S_OUT_OF_MEMORY);
  88. }
  89. InitializeCriticalSection( &(AllocationContext->CriticalSection) );
  90. AllocationContext->ClientAlloc = DefaultAllocate;
  91. AllocationContext->ClientFree = DefaultFree;
  92. AllocationContext->EnableCount = 0;
  93. SYSTEM_INFO SystemInfo;
  94. AllocationContext->ThreadCount = 1;
  95. AllocationContext->pInitialStacks = 0;
  96. AllocationContext->pEnableStack = 0;
  97. AllocationContext->pBlockDescr = 0;
  98. GetSystemInfo( &SystemInfo );
  99. AllocationContext->PageSize = SystemInfo.dwPageSize;
  100. AllocationContext->Granularity = SystemInfo.dwAllocationGranularity;
  101. SetAllocContext( AllocationContext ); // Tls
  102. }
  103. return( AllocationContext );
  104. }
  105. // -----------------------------------------------------------------
  106. void *
  107. FindBlockForTheChunk(
  108. PALLOCATION_CONTEXT AllocationContext,
  109. size_t Size )
  110. /*++
  111. Routine Description:
  112. This routine returns the pointer to the allocated chunk of memory.
  113. If it cannot allocate a chunk it returns NULL.
  114. This is used only in Win32 version.
  115. Note:
  116. This is called within the critical section.
  117. --*/
  118. {
  119. char * AllocationBlock;
  120. DWORD SizeToAllocate;
  121. unsigned long i, BlockSize;
  122. PALLOC_BLOCK_DESCR pDescrTemp;
  123. char * pChunk;
  124. // Round the size to a multiple of 8, so that
  125. // each memory chunk is always on an aligned by 8 boundary.
  126. Size = ALIGN_TO_8( Size );
  127. // If the size is 0, allocate 8 bytes anyways to guarantee uniqueness
  128. // and to prevent aliasing problems.
  129. if ( Size == 0 )
  130. Size = 8;
  131. // See if the chunk can be allocated within an existing block.
  132. // We use the first fit algorithm to do that.
  133. BlockSize = AllocationContext->PageSize;
  134. if ( Size < BlockSize )
  135. {
  136. for ( i = 0; i < AllocationContext->FFIndex; i++ )
  137. {
  138. pDescrTemp = & AllocationContext->pBlockDescr[i];
  139. if ( pDescrTemp->SizeLeft >= Size )
  140. {
  141. pChunk = pDescrTemp->FirstFree;
  142. pDescrTemp->FirstFree += Size;
  143. pDescrTemp->SizeLeft -= Size;
  144. #if defined( DEBUGRPC )
  145. pDescrTemp->Counter ++;
  146. #endif
  147. return( pChunk );
  148. }
  149. }
  150. // Doesn't fit anywhere: allocate a new block.
  151. SizeToAllocate = BlockSize;
  152. }
  153. else
  154. {
  155. // Size is too big to fit in leftovers.
  156. // Round it up to the block size boundary.
  157. size_t Alignment = BlockSize - 1;
  158. SizeToAllocate = (Size + Alignment) & ~Alignment;
  159. }
  160. //
  161. // Being here means a need to allocate a new block of pages.
  162. //
  163. if ( AllocationContext->FFIndex >= AllocationContext->DescrSize )
  164. {
  165. // need to reallocate the array of descriptors first.
  166. size_t NewDescrSize = AllocationContext->DescrSize +
  167. DESCR_ARRAY_INCR;
  168. pDescrTemp = (PALLOC_BLOCK_DESCR)
  169. I_RpcAllocate( NewDescrSize * sizeof( ALLOC_BLOCK_DESCR ));
  170. if ( pDescrTemp == 0 )
  171. {
  172. return( NULL );
  173. }
  174. RpcpMemoryCopy( pDescrTemp,
  175. AllocationContext->pBlockDescr,
  176. (size_t)(AllocationContext->FFIndex
  177. * sizeof( ALLOC_BLOCK_DESCR )) );
  178. if ( AllocationContext->pBlockDescr !=
  179. AllocationContext->pInitialStacks->DescrStack )
  180. I_RpcFree( AllocationContext->pBlockDescr );
  181. AllocationContext->pBlockDescr = pDescrTemp;
  182. AllocationContext->DescrSize = NewDescrSize;
  183. }
  184. // Now allocate the new block.
  185. AllocationBlock = (char *) VirtualAlloc( NULL, // new pages
  186. SizeToAllocate,
  187. MEM_COMMIT,
  188. PAGE_READWRITE );
  189. if ( AllocationBlock == 0 )
  190. {
  191. return( NULL );
  192. }
  193. NDR_ASSERT( ((ULONG_PTR)AllocationBlock & 0x7) == 0,
  194. "buffer alignment error at allocation time" );
  195. pDescrTemp = & AllocationContext->pBlockDescr[ AllocationContext->FFIndex ];
  196. pDescrTemp->AllocationBlock = AllocationBlock;
  197. pDescrTemp->FirstFree = AllocationBlock + Size;
  198. pDescrTemp->SizeLeft = SizeToAllocate - Size;
  199. #if defined( DEBUGRPC )
  200. pDescrTemp->Counter = 1;
  201. #endif
  202. AllocationContext->FFIndex++;
  203. return( AllocationBlock );
  204. }
  205. void * RPC_ENTRY
  206. RpcSsAllocate (
  207. IN size_t Size
  208. )
  209. /*++
  210. Routine Description:
  211. Allocate memory within the allocation context fot the thread. A call to
  212. RpcSsEnableAllocate sets up an allocation context for the calling thread.
  213. When the application (and/or the stubs) call RpcSsDisableAllocate,
  214. any memory allocated by RpcSsAllocate in the context which has not been
  215. freed (by RpcSsFree) will be freed, and the context will be freed.
  216. Arguments:
  217. Size - Supplies the amount of memory required in bytes.
  218. Return Value:
  219. A pointer to the allocated block of memory will be returned.
  220. Note:
  221. This is Win32 version.
  222. Exceptions:
  223. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  224. will be raised.
  225. RPC_S_INVALID_ARG - If no allocation context yet.
  226. --*/
  227. {
  228. void * AllocatedChunk = 0;
  229. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  230. if ( AllocationContext == 0 )
  231. {
  232. RpcRaiseException( RPC_S_INVALID_ARG );
  233. }
  234. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  235. if ( AllocationContext->EnableCount )
  236. AllocatedChunk = FindBlockForTheChunk( AllocationContext, Size );
  237. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  238. if ( AllocatedChunk == NULL )
  239. {
  240. RpcRaiseException(RPC_S_OUT_OF_MEMORY);
  241. }
  242. return( AllocatedChunk );
  243. }
  244. static void
  245. NdrpReleaseMemory(
  246. PALLOCATION_CONTEXT AllocationContext
  247. )
  248. /*++
  249. Routine Description:
  250. Releases all the memory related to the package except for the
  251. control block itself.
  252. Note:
  253. This is Win32 version and is called from the critical section
  254. in disable allocate and set thread.
  255. --*/
  256. {
  257. unsigned long i;
  258. for ( i = 0; i < AllocationContext->FFIndex; i++ )
  259. {
  260. VirtualFree( AllocationContext->pBlockDescr[i].AllocationBlock,
  261. 0, // free all of it
  262. MEM_RELEASE );
  263. }
  264. I_RpcFree( AllocationContext->pInitialStacks );
  265. if ( AllocationContext->pEnableStack !=
  266. AllocationContext->pInitialStacks->EnableStack )
  267. I_RpcFree( AllocationContext->pEnableStack );
  268. if ( AllocationContext->pBlockDescr !=
  269. AllocationContext->pInitialStacks->DescrStack )
  270. I_RpcFree( AllocationContext->pBlockDescr );
  271. AllocationContext->pInitialStacks = 0;
  272. AllocationContext->pEnableStack = 0;
  273. AllocationContext->StackMax = 0;
  274. AllocationContext->StackTop = 0;
  275. AllocationContext->pBlockDescr = 0;
  276. AllocationContext->DescrSize = 0;
  277. AllocationContext->FFIndex = 0;
  278. AllocationContext->ClientAlloc = DefaultAllocate;
  279. AllocationContext->ClientFree = DefaultFree;
  280. NDR_ASSERT( AllocationContext->ThreadCount, "when relesing all memory" );
  281. AllocationContext->ThreadCount--;
  282. }
  283. static void
  284. NdrpDisableAllocate(
  285. BOOL fCalledFromStub
  286. )
  287. /*++
  288. Routine Description:
  289. Multiple enable/disable are allowed and the EnableCount is used to keep
  290. track of it.
  291. We disable only when counter comes back to 0.
  292. We ignore too many disables.
  293. This routine will free memory associated with the allocation context
  294. for this thread as well as the allocation context.
  295. However, the routine frees only blocks allocated with the default
  296. allocator. Other blocks are considered to be allocated
  297. with a swapped/set user allocator and so we leave them alone.
  298. Allocation context gets freed when process detaches.
  299. Note:
  300. This is Win32 version.
  301. --*/
  302. {
  303. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  304. unsigned long NewLevel;
  305. BOOL fTooManyDisables = FALSE;
  306. BOOL fLastThreadGoingAway = FALSE;
  307. if ( AllocationContext == 0 )
  308. return;
  309. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  310. if ( fCalledFromStub )
  311. {
  312. NDR_ASSERT( AllocationContext->StackTop, "mismatch in stub calls" );
  313. NewLevel = AllocationContext
  314. ->pEnableStack[ -- AllocationContext->StackTop ];
  315. }
  316. else
  317. NewLevel = AllocationContext->EnableCount - 1;
  318. // We are forcing the EnableCount to get the value from the EnableStack,
  319. // when possible.
  320. if ( AllocationContext->EnableCount )
  321. AllocationContext->EnableCount = NewLevel;
  322. else
  323. {
  324. // doesn't fall below zero.
  325. fTooManyDisables = TRUE;
  326. }
  327. if ( AllocationContext->EnableCount == 0 )
  328. {
  329. // First free everything except the control block.
  330. if ( !fTooManyDisables )
  331. NdrpReleaseMemory( AllocationContext );
  332. // Now see if we need to free the context itself.
  333. // Because of the thread reusage in the runtime, and consequently,
  334. // some threads hanging around forever, we need to dispose
  335. // of this, even though it costs a new allocation later.
  336. if ( AllocationContext->ThreadCount == 0 )
  337. {
  338. fLastThreadGoingAway = TRUE;
  339. SetAllocContext( NULL );
  340. }
  341. }
  342. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  343. if ( fLastThreadGoingAway)
  344. {
  345. DeleteCriticalSection( &(AllocationContext->CriticalSection) );
  346. I_RpcFree( AllocationContext );
  347. }
  348. }
  349. void RPC_ENTRY
  350. RpcSsDisableAllocate (
  351. void
  352. )
  353. /*++
  354. Routine Description:
  355. See the description of NdrpDisableAllocate.
  356. Note:
  357. This is Win32 version.
  358. --*/
  359. {
  360. NdrpDisableAllocate( FALSE );
  361. }
  362. static void
  363. NdrpEnableAllocate (
  364. BOOL fCalledFromStub
  365. )
  366. /*++
  367. Routine Description:
  368. This routine will set up an allocation context for this thread.
  369. Note:
  370. The behavior is such that it is valid to call EnableAllocate
  371. several times in a row without calling DisableAllocate.
  372. The number to calls to Disable has to much that of Enable.
  373. This is Win32 version.
  374. Exceptions:
  375. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  376. will be raised.
  377. --*/
  378. {
  379. PALLOCATION_CONTEXT AllocationContext = GetCreateAllocationContext();
  380. int Successful = 1;
  381. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  382. if ( AllocationContext->EnableCount == 0 )
  383. {
  384. AllocationContext->pInitialStacks = (INIT_STACKS_BLOCK *)
  385. I_RpcAllocate( sizeof( INIT_STACKS_BLOCK ) );
  386. if ( AllocationContext->pInitialStacks )
  387. {
  388. AllocationContext->pEnableStack =
  389. AllocationContext->pInitialStacks->EnableStack;
  390. AllocationContext->StackMax = ENABLE_STACK_SIZE;
  391. AllocationContext->StackTop = 0;
  392. AllocationContext->pBlockDescr =
  393. AllocationContext->pInitialStacks->DescrStack;
  394. AllocationContext->DescrSize = DESCR_ARRAY_SIZE;
  395. AllocationContext->FFIndex = 0;
  396. AllocationContext->ClientAlloc = RpcSsAllocate;
  397. AllocationContext->ClientFree = RpcSsFree;
  398. }
  399. else
  400. Successful = 0;
  401. }
  402. if ( Successful )
  403. {
  404. if ( fCalledFromStub )
  405. {
  406. // Push the current enable level on the EnableStack to have
  407. // a point to come back when disabling from the server stub.
  408. if ( AllocationContext->StackTop >= AllocationContext->StackMax )
  409. {
  410. // Need to reallocate the EnableStack first.
  411. ulong NewMax;
  412. ulong * pStackTemp;
  413. NewMax = AllocationContext->StackMax + ENABLE_STACK_SIZE;
  414. pStackTemp = (ulong *) I_RpcAllocate( NewMax * sizeof(ulong) );
  415. if ( pStackTemp )
  416. {
  417. RpcpMemoryCopy( pStackTemp,
  418. AllocationContext->pEnableStack,
  419. AllocationContext->StackMax * sizeof(ulong) );
  420. if ( AllocationContext->pEnableStack !=
  421. AllocationContext->pInitialStacks->EnableStack )
  422. I_RpcFree( AllocationContext->pEnableStack );
  423. AllocationContext->pEnableStack = pStackTemp;
  424. AllocationContext->StackMax = NewMax;
  425. }
  426. else
  427. Successful = 0;
  428. }
  429. if ( Successful )
  430. AllocationContext->pEnableStack[ AllocationContext->StackTop++ ]
  431. = AllocationContext->EnableCount;
  432. else
  433. if ( AllocationContext->EnableCount == 0 )
  434. {
  435. // just allocated the stuff ..
  436. I_RpcFree( AllocationContext->pInitialStacks );
  437. AllocationContext->pInitialStacks = 0;
  438. AllocationContext->pEnableStack = 0;
  439. AllocationContext->pBlockDescr = 0;
  440. }
  441. }
  442. // Increment the counter to a new level.
  443. if ( Successful )
  444. {
  445. AllocationContext->EnableCount++;
  446. }
  447. }
  448. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  449. if ( ! Successful )
  450. {
  451. RpcRaiseException(RPC_S_OUT_OF_MEMORY);
  452. }
  453. }
  454. void RPC_ENTRY
  455. RpcSsEnableAllocate (
  456. void
  457. )
  458. /*++
  459. Routine Description:
  460. This routine will set up an allocation context for this thread.
  461. Note:
  462. The behavior is such that it is valid to call EnableAllocate
  463. several times in a row without calling DisableAllocate.
  464. The number to calls to Disable should much that of Enable.
  465. This is Win32 version.
  466. Exceptions:
  467. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  468. will be raised.
  469. --*/
  470. {
  471. NdrpEnableAllocate( FALSE );
  472. }
  473. void RPC_ENTRY
  474. RpcSsFree (
  475. IN void * NodeToFree
  476. )
  477. /*++
  478. Routine Description:
  479. When a block of memory allocated by RpcSsAllocate is no longer needed,
  480. it can be freed using RpcSsFree.
  481. Actually, for win32 we do nothing
  482. - all blocks will be freed at the Disable time as we want speed.
  483. Arguments:
  484. NodeToFree - Supplies the block of memory, allocated by RpcSsAllocate, to
  485. be freed.
  486. Note:
  487. This is Win32 version.
  488. --*/
  489. {
  490. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  491. if ( AllocationContext == 0 )
  492. {
  493. #if defined( DEBUGRPC )
  494. RpcRaiseException( RPC_S_INVALID_ARG );
  495. #else
  496. return;
  497. #endif
  498. }
  499. }
  500. // -----------------------------------------------------------------
  501. RPC_SS_THREAD_HANDLE RPC_ENTRY
  502. RpcSsGetThreadHandle (
  503. void
  504. )
  505. /*++
  506. Return Value:
  507. A handle to the allocation context for this thread will be returned.
  508. This makes it possible for two threads to share an allocation context.
  509. See RpcSsSetThreadHandle as well.
  510. != NULL - only when the environement is actually enabled.
  511. --*/
  512. {
  513. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  514. RPC_SS_THREAD_HANDLE Handle = 0;
  515. if ( AllocationContext == 0 )
  516. return( 0 );
  517. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  518. // Check if the memory environement is enabled.
  519. if ( AllocationContext->EnableCount > 0 )
  520. Handle = AllocationContext;
  521. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  522. return( Handle );
  523. }
  524. void RPC_ENTRY
  525. RpcSsSetClientAllocFree (
  526. IN RPC_CLIENT_ALLOC * ClientAlloc,
  527. IN RPC_CLIENT_FREE * ClientFree
  528. )
  529. /*++
  530. Routine Description:
  531. The routines to be used by the client to allocate and free memory can
  532. be set using this routine. See also RpcSsSwapClientAllocFree.
  533. Arguments:
  534. ClientAlloc - Supplies the routine to use to allocate memory.
  535. ClientFree - Supplies the routine to use to free memory.
  536. Exceptions:
  537. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  538. will be raised.
  539. Note. Back door to enable.
  540. DCE (DEC's intrepretation of DCE) has this weird requirement that
  541. the user can set (or swap in) his private allocator pair without first
  542. enabling the package. This makes it possible for the client side stub
  543. to use a private allocator (instead of malloc/free (in osf mode)) to
  544. allocate when unmarshalling.
  545. However, that doesn't enable the package. So, issuing a regular API like
  546. RpcSsAllocate still fails.
  547. The expected behavior is to Enable the package, set or swap private
  548. allocators and then RpcSsAllocate is a valid call.
  549. This has some impact on the disable behavior. The implementation allows
  550. for multiple enables and disables. To prevent leaking/passing on an
  551. non-empty memory context with a thread when the thread is reused,
  552. there is a control mechanism (the enable stack) that makes sure that
  553. disbling from the stubs resets the enable level to the one at the
  554. enable time.
  555. Now, when the (server) stub issues enable, the corresponding disable
  556. will do the right thing, regardless of what the user might have messed up.
  557. If there weren't eanough disables, it will free everything; if there were
  558. too many disables, it would quietly do nothing.
  559. When the user issues an enable, the matching disable will clean up
  560. everything. If there is insufficient number of disable call on the client,
  561. there is a leak and we cannot do anything about it. Too many calls are ok.
  562. Now at last. If the user issues set/swap, an (empty) context is created
  563. with the disabled state. If the package has never been enabled, this empty
  564. context won't be deleted by a disable (this would be a disable too many).
  565. This is harmul both on client and server so I leave it as it is.
  566. --*/
  567. {
  568. PALLOCATION_CONTEXT AllocationContext = GetCreateAllocationContext();
  569. // The only reason we enter the critical section here is to keep
  570. // the pair consistent.
  571. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  572. AllocationContext->ClientAlloc = ClientAlloc;
  573. AllocationContext->ClientFree = ClientFree;
  574. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  575. }
  576. void RPC_ENTRY
  577. RpcSsSetThreadHandle (
  578. IN RPC_SS_THREAD_HANDLE Id
  579. )
  580. /*++
  581. Routine Description:
  582. The allocation context for this thread will set to be the supplied
  583. allocation context.
  584. For 32bit environment, the ThreadReCount is updated.
  585. Arguments:
  586. Id - Supplies the allocation context to use for this thread.
  587. Exceptions:
  588. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  589. will be raised.
  590. --*/
  591. {
  592. // This is 32bit and 64b code.
  593. // DCE doesn't really specify any means to support some kind of thread
  594. // ref counting and so additional semantics has been defined for this
  595. // API.
  596. // In order to define some orderly behavior for the apps passing
  597. // thread handles around, we keep track of how many threads can
  598. // access an allocation context. For an app to be clean, each thread
  599. // that issues set call(s) with non-zero argument should signal it's
  600. // done with a single set call with 0.
  601. // The ThreadCount has a special flavor here, which is to
  602. // count the threads that can access the context, not the references
  603. // in the sense of set calls.
  604. // The original thread doesn't issue a set but we still have to
  605. // account for it. So we start the ThreadCount with 1, and then
  606. // the last disable (the one that frees the memory) decrements the count.
  607. // Which means that the thread issuing the last disable effectively
  608. // does a set to zero at the same time.
  609. // The rules below support decrementing the ThreadCount by means
  610. // of calling the routine with thread id == 0, or with thread id being
  611. // different from the current one.
  612. // If this happens to be the call that would remove the last chance
  613. // to reference the context, we force to free everything, including
  614. // the control block.
  615. // In other words, set 0 on the last thread does the disable.
  616. PALLOCATION_CONTEXT pOldContext = GetAllocContext();
  617. PALLOCATION_CONTEXT pNewContext = (PALLOCATION_CONTEXT) Id;
  618. if ( pOldContext != pNewContext )
  619. {
  620. if ( pOldContext )
  621. {
  622. BOOL fLastThreadGoingAway = FALSE;
  623. EnterCriticalSection( &(pOldContext->CriticalSection) );
  624. if ( pOldContext->ThreadCount == 1 )
  625. {
  626. // delete the memory and decrease the ref count
  627. NdrpReleaseMemory( pOldContext );
  628. fLastThreadGoingAway = TRUE;
  629. SetAllocContext( NULL );
  630. }
  631. LeaveCriticalSection( &(pOldContext->CriticalSection) );
  632. if ( fLastThreadGoingAway)
  633. {
  634. DeleteCriticalSection( &(pOldContext->CriticalSection) );
  635. I_RpcFree( pOldContext );
  636. }
  637. }
  638. if ( pNewContext )
  639. {
  640. EnterCriticalSection( &(pNewContext->CriticalSection) );
  641. pNewContext->ThreadCount++;
  642. LeaveCriticalSection( &(pNewContext->CriticalSection) );
  643. }
  644. }
  645. SetAllocContext( pNewContext );
  646. }
  647. void RPC_ENTRY
  648. RpcSsSwapClientAllocFree (
  649. IN RPC_CLIENT_ALLOC * ClientAlloc,
  650. IN RPC_CLIENT_FREE * ClientFree,
  651. OUT RPC_CLIENT_ALLOC * * OldClientAlloc,
  652. OUT RPC_CLIENT_FREE * * OldClientFree
  653. )
  654. /*++
  655. Routine Description:
  656. The routines to be used by the client to allocate and free memory can
  657. be set using this routine. The previous values of these routines will
  658. be returned. See also RpcSsSetClientAllocFree.
  659. Arguments:
  660. ClientAlloc - Supplies the routine to use to allocate memory.
  661. ClientFree - Supplies the routine to use to free memory.
  662. OldClientAlloc - Returns the old value of the client allocator.
  663. OldClientFree - Returns the old value of the client deallocator.
  664. Exceptions:
  665. RPC_S_OUT_OF_MEMORY - If insufficient memory is available, this exception
  666. will be raised.
  667. --*/
  668. {
  669. PALLOCATION_CONTEXT AllocationContext = GetCreateAllocationContext();
  670. // The only reason we enter the critical section here is to keep
  671. // the pairs consistent.
  672. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  673. *OldClientAlloc = AllocationContext->ClientAlloc;
  674. *OldClientFree = AllocationContext->ClientFree;
  675. AllocationContext->ClientAlloc = ClientAlloc;
  676. AllocationContext->ClientFree = ClientFree;
  677. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  678. }
  679. /*++ -----------------------------------------------------------------------
  680. //
  681. // RpcSm* functions are wrappers over RpcSs*
  682. //
  683. // What was earlier: a hen or an egg?
  684. // We wrap RpcSm* over RpcSs* because RpcSs* are a basic staple for stubs
  685. // and so this makes the critical path shorter.
  686. // Admittedly, RpcSm* take then longer than they could.
  687. //
  688. --*/
  689. void * RPC_ENTRY
  690. RpcSmAllocate (
  691. IN size_t Size,
  692. OUT RPC_STATUS * pStatus
  693. )
  694. /*++
  695. Routine Description:
  696. Same as RpcSsAllocate, except that this one returns an error code,
  697. as opposed to raising an exception.
  698. Arguments:
  699. Size - Supplies the amount of memory required in bytes.
  700. pStatus - Returns an error code:
  701. RPC_S_OK or RPC_S_OUT_OF_MEMORY
  702. Return Value:
  703. A pointer to the allocated block of memory or NULL will be returned.
  704. Exceptions:
  705. This routine catches exceptions and returns an error code.
  706. --*/
  707. {
  708. void * AllocatedNode = 0;
  709. RpcTryExcept
  710. {
  711. AllocatedNode = RpcSsAllocate( Size );
  712. *pStatus = RPC_S_OK;
  713. }
  714. RpcExcept(1)
  715. {
  716. *pStatus = RpcExceptionCode();
  717. }
  718. RpcEndExcept
  719. return( AllocatedNode );
  720. }
  721. RPC_STATUS RPC_ENTRY
  722. RpcSmClientFree (
  723. IN void * pNodeToFree
  724. )
  725. /*++
  726. Routine Description:
  727. Same as RpcSsClientFree, except that this one returns an error code,
  728. as opposed to raising an exception.
  729. Arguments:
  730. pNodeToFree - a memory chunk to free
  731. Return Value:
  732. error code - RPC_S_OK or exception code
  733. Exceptions:
  734. This routine catches exceptions and returns an error code.
  735. --*/
  736. {
  737. RPC_STATUS Status = RPC_S_OK;
  738. RpcTryExcept
  739. {
  740. NdrRpcSmClientFree( pNodeToFree );
  741. }
  742. RpcExcept(1)
  743. {
  744. Status = RpcExceptionCode();
  745. }
  746. RpcEndExcept
  747. return( Status );
  748. }
  749. RPC_STATUS RPC_ENTRY
  750. RpcSmDisableAllocate (
  751. void
  752. )
  753. /*++
  754. Routine Description:
  755. Same as RpcSsDisableAllocate, except that this one returns an error code,
  756. as opposed to raising an exception.
  757. Return Value:
  758. error code - RPC_S_OK or exception code
  759. Exceptions:
  760. Exceptions are catched and an error code is returned.
  761. --*/
  762. {
  763. RPC_STATUS Status = RPC_S_OK;
  764. RpcTryExcept
  765. {
  766. RpcSsDisableAllocate();
  767. }
  768. RpcExcept(1)
  769. {
  770. Status = RpcExceptionCode();
  771. }
  772. RpcEndExcept
  773. return( Status );
  774. }
  775. RPC_STATUS RPC_ENTRY
  776. RpcSmDestroyClientContext (
  777. IN void * * pContextHandle
  778. )
  779. /*++
  780. Routine Description:
  781. Frees the memory related to unused context handle.
  782. Arguments:
  783. ContextHandle - a context handle to be destroyed
  784. Return Value:
  785. error code - RPC_S_OK or exception code
  786. Exceptions:
  787. This routine catches exceptions and returns an error code.
  788. --*/
  789. {
  790. RPC_STATUS Status = RPC_S_OK;
  791. RpcTryExcept
  792. {
  793. RpcSsDestroyClientContext( pContextHandle );
  794. }
  795. RpcExcept(1)
  796. {
  797. Status = RpcExceptionCode();
  798. }
  799. RpcEndExcept
  800. return( Status );
  801. }
  802. RPC_STATUS RPC_ENTRY
  803. RpcSmEnableAllocate (
  804. void
  805. )
  806. /*++
  807. Routine Description:
  808. Same as RpcSsEnableAllocate, except that this one returns an error code,
  809. as opposed to raising an exception.
  810. Exceptions:
  811. Exceptions are catched and an error code is returned.
  812. --*/
  813. {
  814. RPC_STATUS Status = RPC_S_OK;
  815. RpcTryExcept
  816. {
  817. RpcSsEnableAllocate();
  818. }
  819. RpcExcept(1)
  820. {
  821. Status = RpcExceptionCode();
  822. }
  823. RpcEndExcept
  824. return( Status );
  825. }
  826. RPC_STATUS RPC_ENTRY
  827. RpcSmFree (
  828. IN void * NodeToFree
  829. )
  830. /*++
  831. Routine Description:
  832. Same as RpcSsFree, except that this one returns an error code,
  833. as opposed to raising an exception.
  834. Arguments:
  835. NodeToFree - Supplies the block of memory, allocated by RpcSmAllocate, to
  836. be freed.
  837. --*/
  838. {
  839. RPC_STATUS Status = RPC_S_OK;
  840. RpcTryExcept
  841. {
  842. RpcSsFree( NodeToFree );
  843. }
  844. RpcExcept(1)
  845. {
  846. Status = RpcExceptionCode();
  847. }
  848. RpcEndExcept
  849. return( Status );
  850. }
  851. RPC_SS_THREAD_HANDLE RPC_ENTRY
  852. RpcSmGetThreadHandle (
  853. OUT RPC_STATUS * pStatus
  854. )
  855. /*++
  856. Arguments:
  857. pStatus - error code
  858. Return Value:
  859. Same as RpcSsGetThreadHandle, except that this one returns an error code,
  860. as opposed to raising an exception.
  861. --*/
  862. {
  863. RPC_SS_THREAD_HANDLE Handle = 0;
  864. *pStatus = RPC_S_OK;
  865. RpcTryExcept
  866. {
  867. Handle = RpcSsGetThreadHandle();
  868. }
  869. RpcExcept(1)
  870. {
  871. *pStatus = RpcExceptionCode();
  872. }
  873. RpcEndExcept
  874. return( Handle );
  875. }
  876. RPC_STATUS RPC_ENTRY
  877. RpcSmSetClientAllocFree (
  878. IN RPC_CLIENT_ALLOC * ClientAlloc,
  879. IN RPC_CLIENT_FREE * ClientFree
  880. )
  881. /*++
  882. Routine Description:
  883. Same as RpcSsSetClientAllocFree, except that this one returns an error code,
  884. as opposed to raising an exception.
  885. Arguments:
  886. ClientAlloc - Supplies the routine to use to allocate memory.
  887. ClientFree - Supplies the routine to use to free memory.
  888. Return Value:
  889. error code - RPC_S_OK or exception code
  890. Exceptions:
  891. Exceptions are catched and an error code is returned.
  892. --*/
  893. {
  894. RPC_STATUS Status = RPC_S_OK;
  895. RpcTryExcept
  896. {
  897. RpcSsSetClientAllocFree( ClientAlloc, ClientFree );
  898. }
  899. RpcExcept(1)
  900. {
  901. Status = RpcExceptionCode();
  902. }
  903. RpcEndExcept
  904. return( Status );
  905. }
  906. RPC_STATUS RPC_ENTRY
  907. RpcSmSetThreadHandle (
  908. IN RPC_SS_THREAD_HANDLE Id
  909. )
  910. /*++
  911. Routine Description:
  912. Same as RpcSsSetThreadHandle, except that this one returns an error code,
  913. as opposed to raising an exception.
  914. Arguments:
  915. Id - Supplies the allocation context to use for this thread.
  916. Return Value:
  917. error code - RPC_S_OK or exception code (RPC_S_OUT_OF_MEMORY)
  918. Exceptions:
  919. Exceptions are catched and an error code is returned.
  920. --*/
  921. {
  922. RPC_STATUS Status = RPC_S_OK;
  923. RpcTryExcept
  924. {
  925. RpcSsSetThreadHandle( Id );
  926. }
  927. RpcExcept(1)
  928. {
  929. Status = RpcExceptionCode();
  930. }
  931. RpcEndExcept
  932. return( Status );
  933. }
  934. RPC_STATUS RPC_ENTRY
  935. RpcSmSwapClientAllocFree (
  936. IN RPC_CLIENT_ALLOC * ClientAlloc,
  937. IN RPC_CLIENT_FREE * ClientFree,
  938. OUT RPC_CLIENT_ALLOC * * OldClientAlloc,
  939. OUT RPC_CLIENT_FREE * * OldClientFree
  940. )
  941. /*++
  942. Routine Description:
  943. Same as RpcSsSwapClientAllocFree, except that this one returns an error
  944. code, as opposed to raising an exception.
  945. Arguments:
  946. ClientAlloc - Supplies the routine to use to allocate memory.
  947. ClientFree - Supplies the routine to use to free memory.
  948. OldClientAlloc - Returns the old value of the client allocator.
  949. OldClientFree - Returns the old value of the client deallocator.
  950. Return Value:
  951. error code - RPC_S_OK or exception code (RPC_S_OUT_OF_MEMORY)
  952. Exceptions:
  953. Exceptions are catched and an error code is returned.
  954. --*/
  955. {
  956. RPC_STATUS Status = RPC_S_OK;
  957. RpcTryExcept
  958. {
  959. RpcSsSwapClientAllocFree( ClientAlloc,
  960. ClientFree,
  961. OldClientAlloc,
  962. OldClientFree );
  963. }
  964. RpcExcept(1)
  965. {
  966. Status = RpcExceptionCode();
  967. }
  968. RpcEndExcept
  969. return( Status );
  970. }
  971. // =======================================================================
  972. // Package initialization
  973. // =======================================================================
  974. // default: win32 now
  975. DWORD RpcAllocTlsIndex = 0xFFFFFFFF;
  976. PALLOCATION_CONTEXT
  977. GetAllocContext (
  978. )
  979. /*++
  980. Return Value:
  981. The allocation context pointer for this thread will be returned. Use
  982. SetAllocContext to set the allocation context pointer for this thread.
  983. If GetAllocContext is called before SetAllocContext has been called, zero
  984. will be returned.
  985. --*/
  986. {
  987. if (RpcAllocTlsIndex == 0xFFFFFFFF)
  988. {
  989. GlobalMutexRequestExternal();
  990. if (RpcAllocTlsIndex == 0xFFFFFFFF)
  991. {
  992. RpcAllocTlsIndex = TlsAlloc();
  993. if (RpcAllocTlsIndex == 0xFFFFFFFF)
  994. {
  995. GlobalMutexClearExternal();
  996. RpcRaiseException(RPC_S_OUT_OF_MEMORY);
  997. }
  998. }
  999. GlobalMutexClearExternal();
  1000. }
  1001. return( (PALLOCATION_CONTEXT) TlsGetValue(RpcAllocTlsIndex));
  1002. }
  1003. void
  1004. SetAllocContext (
  1005. PALLOCATION_CONTEXT AllocContext
  1006. )
  1007. /*++
  1008. Arguments:
  1009. AllocContext - Supplies a new allocation context pointer for this thread.
  1010. Use GetAllocContext to retrieve the allocation context pointer for
  1011. a thread.
  1012. --*/
  1013. {
  1014. if ( ! TlsSetValue(RpcAllocTlsIndex, AllocContext) )
  1015. RpcRaiseException( GetLastError() );
  1016. }
  1017. //
  1018. // We can't rely on a call like that in Win32 as some threads hang for ever.
  1019. // The runtime doesn't call us when a thread goes away and into recycling.
  1020. //
  1021. //void
  1022. //NdrRpcDeleteAllocationContext()
  1023. //{
  1024. //}
  1025. // =======================================================================
  1026. // Private entry points for stubs
  1027. // =======================================================================
  1028. //
  1029. void RPC_ENTRY
  1030. NdrRpcSsEnableAllocate(
  1031. PMIDL_STUB_MESSAGE pMessage )
  1032. {
  1033. NdrpEnableAllocate( TRUE );
  1034. pMessage->pfnAllocate = RpcSsAllocate;
  1035. pMessage->pfnFree = RpcSsFree;
  1036. }
  1037. void RPC_ENTRY
  1038. NdrRpcSsDisableAllocate(
  1039. PMIDL_STUB_MESSAGE pMessage )
  1040. {
  1041. NdrpDisableAllocate( TRUE );
  1042. pMessage->pfnAllocate = NdrRpcSsDefaultAllocate;
  1043. pMessage->pfnFree = NdrRpcSsDefaultFree;
  1044. }
  1045. void RPC_ENTRY
  1046. NdrRpcSmSetClientToOsf(
  1047. PMIDL_STUB_MESSAGE pMessage )
  1048. {
  1049. pMessage->pfnAllocate = NdrRpcSmClientAllocate;
  1050. pMessage->pfnFree = NdrRpcSmClientFree;
  1051. }
  1052. void * RPC_ENTRY
  1053. NdrRpcSsDefaultAllocate (
  1054. IN size_t Size
  1055. )
  1056. {
  1057. return I_RpcAllocate( Size );
  1058. }
  1059. void RPC_ENTRY
  1060. NdrRpcSsDefaultFree (
  1061. IN void * NodeToFree
  1062. )
  1063. {
  1064. I_RpcFree( NodeToFree );
  1065. }
  1066. void * RPC_ENTRY
  1067. NdrRpcSmClientAllocate (
  1068. IN size_t Size
  1069. )
  1070. /*++
  1071. This is the client stub private entry point that checks if a memory
  1072. manager has been enabled. If not, a default or a user's private
  1073. allocator is called.
  1074. --*/
  1075. {
  1076. RPC_CLIENT_ALLOC * ClientAlloc;
  1077. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  1078. if ( AllocationContext == 0 )
  1079. {
  1080. return( DefaultAllocate( Size ));
  1081. }
  1082. // User's ClientAlloc may encapsulate a RpcSsAllocate call.
  1083. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  1084. ClientAlloc = AllocationContext->ClientAlloc;
  1085. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  1086. return (*ClientAlloc)( Size );
  1087. }
  1088. void RPC_ENTRY
  1089. NdrRpcSmClientFree (
  1090. IN void * NodeToFree
  1091. )
  1092. {
  1093. RPC_CLIENT_FREE * ClientFree;
  1094. PALLOCATION_CONTEXT AllocationContext = GetAllocContext();
  1095. if ( AllocationContext == 0 )
  1096. {
  1097. DefaultFree( NodeToFree );
  1098. return;
  1099. }
  1100. EnterCriticalSection( &(AllocationContext->CriticalSection) );
  1101. ClientFree = AllocationContext->ClientFree;
  1102. LeaveCriticalSection( &(AllocationContext->CriticalSection) );
  1103. (* ClientFree)( NodeToFree );
  1104. }
  1105. // =======================================================================
  1106. // Private entry point for test
  1107. // =======================================================================
  1108. #if defined( DEBUGRPC )
  1109. void * RPC_ENTRY
  1110. RpcSsGetInfo(
  1111. void )
  1112. {
  1113. return( GetAllocContext() );
  1114. }
  1115. #endif