Problem is, you’re assuming a scope. I read the DDK documentation, where
they say, “Drivers should normally allocate the structure on the stack”,
well, again, that assumes a static scope. This may not be the way a spinlock
is used in real life, it’s nice and well behaved but it may be too
restrictive: the stack at the time the acquirer was created may not exist
any longer, so, in the general case I may need more permanent storage than a
stack: for example, what if thread A acquires a spinlock, forks threads B
through E, and then terminates, so that whoever finishes last, B, C, D or E,
releases the lock ? What if I want to use a spinlock as a barrier
synchronizer, and again, the last thread to arrive at the barrier releases
it ? And if I can delegate that kind of issue to the object itself, why
should I bother handling it ? I also think it’s a bad idea to rely on any
specific time to destroy objects, because well, we may want to implement
garbage collection for example: such a nice language as C# should have a
real compiler that allows us to write drivers, and with automatic garbage
collection you don’t want to mix object destruction with anything else. So,
I believe that releasing an object and deleting it should be two very
separate actions, and the original local context when the object was created
must not be assumed at the time the object is destroyed.
I hear your argument, and it kind of makes sense, but I still believe that
encapsulating everything within a single object is a better way.
Alberto.
-----Original Message-----
From: Don Burn [mailto:xxxxx@acm.org]
Sent: Thursday, September 25, 2003 1:34 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp
uter
I first saw this from Bjarne Stroustrup and the Bell Labs C++ team
many years ago. As Max points out, this has the property (good or
bad depending on your preference) of automatically releasing the
lock as part of the destructor, therefore guaranteeing release at the
end of a scope.
I have since seen this approach used by many of the recognized
gurus of OO design.
Don Burn (MVP, Windows DDK)
Windows 2k/XP/2k3 Filesystem and Driver Consulting
----- Original Message -----
From: “Moreira, Alberto”
To: “Windows System Software Devs Interest List”
Sent: Thursday, September 25, 2003 1:16 PM
Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp uter
> In object oriented design, resources are no longer pure data, they’re now
> objects ! The use of an object is internal to that object, only interfaces
> are published. Otherwise you must give up having a healthy encapsulation,
> just look at that “friend” statement you need in the class. Another
problem
> I see with this design, as I told Max, is that the queue handle is now
> attached to the Acquirer and not to the Lock: if two different pieces of
> code try to acquire the same spinlock, you will end up with two queues,
> which defeats the purpose of using a queued lock.
>
> Alberto.
>
>
> -----Original Message-----
> From: Chuck Batson [mailto:xxxxx@cbatson.com]
> Sent: Thursday, September 25, 2003 12:46 PM
> To: Windows System Software Devs Interest List
> Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp
> uter
>
>
> > That’s an interesting statement, Chuck, what advantages do you see in
> > separating these two ?
>
> The conceptual separation of a resource from its use. We can use the
> current discussion as the basis for an example:
>
> class CSpinLockAcquire;
>
> class CSpinLock
> {
> public:
> CSpinLock() { KeInitializeSpinLock(&SpinLock); }
> private:
> Lock(KLOCK_QUEUE_HANDLE & h) {
> KeAcquireInStackQueuedSpinLock(&SpinLock, &h); }
> Unlock(KLOCK_QUEUE_HANDLE & h) {
> KeReleaseInStackQueuedSpinLock(&SpinLock, &h); }
> KSPIN_LOCK SpinLock;
> friend class CSpinLockAcquire;
> };
>
> class CSpinLockAcquire
> {
> public:
> CSpinLockAcquire(CSpinLock & s) : SpinLock(s) {
> s.Lock(Handle); }
> ~CSpinLockAcquire() { s.Unlock(Handle); }
> private:
> CSpinLock & SpinLock;
> KLOCK_QUEUE_HANDLE Handle;
> };
>
> This conceptual separation is useful any time you have a resource which
> must be acquired/released, locked/unlocked, etc. There are a few
> concrete benefits:
>
> 1. Exception safety. Since locking and unlocking is handled by the
> constructor/destructor of CSpinLockAcquire, you can never have a
> situation of a lock not being matched with a corresponding unlock, even
> in the presence of exceptions.
>
> 2. Human error and maintainability. For the same reason, it’s
> impossible for the programmer to “forget” to unlock a resource.
> Granted, if you forget to release a spin lock, you’re likely to know
> pretty quickly, but it still wastes time and effort diagnosing the
> problem. The same general principle applies to other types of resources
> (e.g. not necessarily spin locks) where usage may be more involved
> and/or subtle and hence more prone to difficult-to-diagnose “forgetting
> to unlock” errors.
>
> 3. Acquisition/release data is separate from resource data. This is
> good for two reasons:
>
> a. Memory storage for the resource object itself may be precious.
> Consider the “single class” implementation where the spin lock and its
> queue handle are stored together. The spin lock has to go into shared
> memory which is accessible to all those who might potentially acquire
> it. There’s no reason for the queue handle to be shared, since it’s
> only used by one acquirer at a time. So separating them is beneficial
> when use of shared memory has performance implications.
>
> b. Resource acquisition data sometimes must be separate from the
> resource, especially when it’s possible for more that one entity to
> acquire a given resource at the same time. This is less useful in this
> case, when only one processor can acquire a spinlock at any given time.
> (Though it’s still a bit aesthetically displeasing to me, when you think
> about two different processors calling KeAcquireInStackQueuedSpinLock()
> with a pointer to the same queue handle; “luckily” only one of them will
> succeed in acquiring the spin lock, so there will never be more than one
> user of the queue handle… but still makes me shudder.) But there are
> situations in which this is necessary. For example, a resource which
> has both read and write acquires, where multiple readers are allowed but
> only a single writer (with no readers) is allowed. Since you can have
> multiple readers, obviously it won’t do to put the acquisition handle
> (or whatever relevant acquisition-related information) in the resource
> object itself.
>
> Chuck
>
>
> —
> Questions? First check the Kernel Driver FAQ at
> http://www.osronline.com/article.cfm?id=256
>
> You are currently subscribed to ntdev as: xxxxx@compuware.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com
>
>
>
> The contents of this e-mail are intended for the named addressee only. It
> contains information that may be confidential. Unless you are the named
> addressee or an authorized designee, you may not copy or use it, or
disclose
> it to anyone else. If you received it in error please notify us
immediately
> and then destroy it.
>
>
> —
> Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
>
> You are currently subscribed to ntdev as: xxxxx@acm.org
> To unsubscribe send a blank email to xxxxx@lists.osr.com
—
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
You are currently subscribed to ntdev as: xxxxx@compuware.com
To unsubscribe send a blank email to xxxxx@lists.osr.com
The contents of this e-mail are intended for the named addressee only. It
contains information that may be confidential. Unless you are the named
addressee or an authorized designee, you may not copy or use it, or disclose
it to anyone else. If you received it in error please notify us immediately
and then destroy it.