Re: Problem using queued spinlocks on single cpu comp uter

Personal preference, nothing else. I like to grab this kind of object memory
dynamically rather than declare them statically. I like data in the stack
and in the heap, but not necessarily elsewhere !

Alberto.

-----Original Message-----
From: Chuck Batson [mailto:xxxxx@cbatson.com]
Sent: Thursday, September 25, 2003 11:36 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp
uter

class CSpinLock
{
private:
KLOCK_QUEUE_HANDLE *m_psLockQueue;
PKSPIN_LOCK m_oSpinLock;
CString *m_name;
public:
CSpinLock (name)
{
KeInitializeSpinLock(&m_oSpinLock);
m_psLockQueue = new KLOCK_QUEUE_HANDLE;
m_name = new CString(name);
}

Why are these pointers, and why are you allocating with new? Why not
just make them non-pointer members? Am I missing something?

class CSpinLock
{
private:
KLOCK_QUEUE_HANDLE m_LockQueue;
PKSPIN_LOCK m_oSpinLock;
CString m_name;
public:
CSpinLock (const CString & name)
: m_name(name)
{
KeInitializeSpinLock(&m_oSpinLock);
}

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.

Must be a question of personal preference, because I don’t see why

CSpinLockAcquisitor(&SomeGlobalLock);

is better than

SomeGlobalLock.Acquire( );

In other words, Acquire and Release are an integral part of the CSpinlock’s
public interface. Moreover, I could use this to encapsulate the
implementation, so that only the Spinlock itself knows whether it’s an
ordinary or a queued spinlock. But then, I may be wrong.

Alberto.

-----Original Message-----
From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
Sent: Thursday, September 25, 2003 12:18 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp uter

CSpinLock will be the lock itself, and “acquisitor” is a helper object used
like:

CSpinLock SomeGlobalLock;

f()
{
// The locked code path
{
CSpinLockAcquisitor(&SomeGlobalLock);

// End of locked path
}
}

The destructor will release the spinlock.

Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

----- Original Message -----
From: “Moreira, Alberto”
To: “Windows System Software Devs Interest List”
Sent: Thursday, September 25, 2003 7:36 PM
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 ?
>
> Alberto.
>
>
> -----Original Message-----
> From: Chuck Batson [mailto:xxxxx@cbatson.com]
> Sent: Thursday, September 25, 2003 11:09 AM
> To: Windows System Software Devs Interest List
> Subject: [ntdev] Re: Problem using queued spinlocks on single cpu computer
>
>
> > CSpinLock::CSpinLock()
>
> By the way, a better design is to use two separate classes: one for the
> spin lock object itself, and another for acquisition/release.
>
> 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@storagecraft.com
> 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.

I don’t understand, I thought the queue was attached to the spinlock ? Not
to the acquisitor ? With this design, if I have two separate pieces of code
that issue two locks to the same spinlock I get two queues, one at each
acquisitor, while what I may want is both accessors to end up at the same
queue, so that they’re serviced in the same sequence they applied their
respective locks.

Alberto.

-----Original Message-----
From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
Sent: Thursday, September 25, 2003 12:23 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Re: Problem using queued spinlocks on single cpu comp
uter

class CSpinLock
{
private:
KLOCK_QUEUE_HANDLE m_LockQueue;
PKSPIN_LOCK m_oSpinLock;
CString m_name;
public:
CSpinLock (const CString & name)
: m_name(name)
{
KeInitializeSpinLock(&m_oSpinLock);
}

This will not work. The correct way is:

class CSpinLock
{
friend class CSpinLockAcquisitor;
public:
CSpinLock() { KeInitializeSpinLock(&m_Lock); }
private:
KSPIN_LOCK m_Lock;
};

class CSpinLockAcquisitor
{
public:
CSpinLockAcquisitor(CSpinLock* Lock) {
KeAcquireInStackQueuedSpinLock(Lock->m_Lock, &m_Locker); }
~CSpinLockAcquisitor { KeReleaseInStackQueuedSpinLock(&m_Locker); }
private:
KLOCK_QUEUE_HANDLE m_Locker;
};

Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.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.

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.