windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/ess3/esslock.txt
2020-09-26 16:20:57 +08:00

139 lines
5.5 KiB
Plaintext

CEssNamespace Locks
===================
There are two namespace locks : level1 and level2.
Level1 is supposed to be a lightweight lock and guards members like
the current state of the namespace and members that must be accessed when
signaling events ( such as the deferred event queue ). Because of the last
point, very little should be done while holding this lock.
Level2 is the heavy handed lock and guards all of the changes to the
provider cache and to the subscription objects ( e.g. binding.h ).
Level2 must always be aquired before level1 if both are needed.
Level2 or Level1 can never be held when making calls to the providers.
This is problematic because level2 is aquired at the top level and the calls
to the providers occur down deep in the provider cache. To handle this, all
calls to providers are scheduled on a Postponed list associatated with the
threads. After the level2 is released, then the provider calls can be made
and the postponed operations are executed. Note that these calls must occur
on the same control path as the one that scheduled them so they cannot be
asynchronously executed.
Level2 can never be held when signaling an event. This is because some
subscriptions can be synchronous and the action taken on notification could
be to call back into ess ( say to cancel a subscription or something ). The
other reason is that it is possible to aquire the level2 when holding a
filter proxy lock and we must avoid the reverse scenario to avoid a deadlock.
ESS Sink Lock
==================
This is a shared lock whose only purpose is to facilitate shutdown of ESS.
Since all public access to ESS is performed through the esssink, this is
where the ess shutdown check is.
Each entry point except shutdown() will ...
1 ) enter the esssink lock with shared access,
2 ) check to see if shutdown has been performed, if so then goto (4)
3 ) perform the op
4 ) then release it.
Shutdown will ..
1 ) aquires the lock for exclusive access
2 ) set shutdown
3 ) release lock
Since the shared lock handles writer starvation, the shutdown op waits for
all current ops to finish, but does not allow any new ones to procede until
it has executed.
Filter Proxy Lock
==================
PURPOSE : To synchronize the signaling of an event through the proxy with
disconnecting the proxy. When disconneting the proxy from the stub, we want
to ensure that all calls currently executing through that proxy are complete.
( We could have used CoDisconnectObject on the stub for the same functionality,
but this would only work when the proxy was in a separate process/apartment
from the stub which is not always the case ).
TYPE : This is a CWbemCriticalSection ( but should be a shared lock so that
the signaling threads requests shared access and the Disconnect() thread was
exclusive access. )
RULES :
Must be aquired before Namespace Level2 Lock. Reason is that the lock MUST
be held across the signaling of an event, for reasons described above. Since
we support synchronous delivery, there is nothing stopping a consumer from
turning around and issuing a request that will grab the level2 lock in the
same namespace. Because of this, we must always ensure that the proxy lock
is aquired BEFORE acquiring the level2 namespace lock.
Provider Exec Line
=========================
PURPOSE :
This is a different sort of sync mechanism. Its really a queue more than
a lock. It allows the user to place requests in a queue and then to execute
them later. The major difference between this and a normal queue is in the
way that requests are fetched from the queue and executed. The exec line
allows multiple threads to fetch requests from the queue and execute them
while still preserving the logical ordering of the requests in the queue.
For example, lets say that there are the following requests placed in the
queue ...
A, B, C <-- rear
Lets say that T1 placed A and B in the queue and T2 placed C in the queue.
Then both threads try to service their requests. This structure would ensure
that A and B completed before C could execute.
The reason for such a sync structure is that we do not make calls to a provider
while holding the namespace lock. So we 'postpone' the requests to the
provider. Later, after releasing the namespace lock, we execute the
'postponed' operations. This structure ensures that execution of those
postponed operations occurs in the same logical order as the namespace
operations.
e.g. If Namespace Op N1 causes Postponed Operation P1. And N2
causes Postponed Operation P2. Then P1 will be executed before P2 even if
the thread handling N2 tries to execute its postponed operations first.
This is the following protocol used with this sync mechanism
1 ) Get In Line - this reserves a place in the line, called a Turn. A turn
is associated with a postponed request. The turn is returned from this
step.
2 ) Wait For Turn - once obtained, the request can be executed.
3 ) End Turn - after the request is executed, the turn is ended thereby
allowing the next turn to execute.
Each provider record has an associated exec line.
RULES : It is illegal to obtain a proxy lock when holding one or more turns
in any exec line. The reason is that is possible that when holding the
proxy lock that we could wait for a turn. ( Just as it is possible when
holding a proxy lock to obtain the namespace lock ). For this reason, if
we allowed waiting for the proxy lock while holding a turn then we'd have
a deadlock issue.