summaryrefslogtreecommitdiff
path: root/src/vm/rwlock.h
blob: dc8e67e6fb76542cf339c3d325b78c53f8034b10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//

// 
//+-------------------------------------------------------------------
//
//  File:       RWLock.h
//
//  Contents:   Reader writer lock implementation that supports the
//              following features
//                  1. Cheap enough to be used in large numbers
//                     such as per object synchronization.
//                  2. Supports timeout. This is a valuable feature
//                     to detect deadlocks
//                  3. Supports caching of events. The allows
//                     the events to be moved from least contentious
//                     regions to the most contentious regions.
//                     In other words, the number of events needed by
//                     Reader-Writer lockls is bounded by the number
//                     of threads in the process.
//                  4. Supports nested locks by readers and writers
//                  5. Supports spin counts for avoiding context switches
//                     on  multi processor machines.
//                  6. Supports functionality for upgrading to a writer
//                     lock with a return argument that indicates
//                     intermediate writes. Downgrading from a writer
//                     lock restores the state of the lock.
//                  7. Supports functionality to Release Lock for calling
//                     app code. RestoreLock restores the lock state and
//                     indicates intermediate writes.
//                  8. Recovers from most common failures such as creation of
//                     events. In other words, the lock mainitains consistent
//                     internal state and remains usable
//
//  Classes:    CRWLock,
//              CStaticRWLock
// 
//--------------------------------------------------------------------

#ifdef FEATURE_RWLOCK
#ifndef _RWLOCK_H_
#define _RWLOCK_H_
#include "common.h"
#include "threads.h"

// If you do define this, make sure you define this in managed as well.
//#define RWLOCK_STATISTICS     0

extern DWORD gdwDefaultTimeout;
extern DWORD gdwDefaultSpinCount;


//+-------------------------------------------------------------------
//
//  Struct:     LockCookie
//
//  Synopsis:   Lock cookies returned to the client
//
//+-------------------------------------------------------------------
typedef struct {
    DWORD dwFlags;
    DWORD dwWriterSeqNum;
    WORD wReaderLevel;
    WORD wWriterLevel;
    DWORD dwThreadID;
} LockCookie;

//+-------------------------------------------------------------------
//
//  Class:      CRWLock
//
//  Synopsis:   Class the implements the reader writer locks. 
// 
//+-------------------------------------------------------------------
class CRWLock : public Object
{
    friend class MscorlibBinder;

public:
    // Constuctor
    CRWLock();

    // Cleanup
    void Cleanup();

    OBJECTHANDLE GetObjectHandle();
    HRESULT CreateOwnerIterator(SIZE_T *pIterator);
    static void GetNextOwner(SIZE_T Iterator, IHostTask **ppOwnerHostTask);
    static void DeleteOwnerIterator(SIZE_T Iterator);

    // Statics that do the core work
    static FCDECL1 (void, StaticPrivateInitialize, CRWLock *pRWLock);
    static FCDECL1 (void, StaticPrivateDestruct, CRWLock *pRWLock);
    static FCDECL2 (void, StaticAcquireReaderLockPublic, CRWLock *pRWLock, DWORD dwDesiredTimeout);
    static FCDECL2 (void, StaticAcquireWriterLockPublic, CRWLock *pRWLock, DWORD dwDesiredTimeout);
    static FCDECL1 (void, StaticReleaseReaderLockPublic, CRWLock *pRWLock);
    static FCDECL1 (void, StaticReleaseWriterLockPublic, CRWLock *pRWLock);
    static FCDECL3 (void, StaticDoUpgradeToWriterLockPublic, CRWLock *pRWLock, LockCookie * pLockCookie, DWORD dwDesiredTimeout);
    static FCDECL2 (void, StaticDowngradeFromWriterLock, CRWLock *pRWLock, LockCookie* pLockCookie);
    static FCDECL2 (void, StaticDoReleaseLock, CRWLock *pRWLock, LockCookie * pLockCookie);
    static FCDECL2 (void, StaticRestoreLockPublic, CRWLock *pRWLock, LockCookie* pLockCookie);
    static FCDECL1 (FC_BOOL_RET, StaticIsReaderLockHeld, CRWLock *pRWLock);
    static FCDECL1 (FC_BOOL_RET, StaticIsWriterLockHeld, CRWLock *pRWLock);
    static FCDECL1 (INT32, StaticGetWriterSeqNum, CRWLock *pRWLock);
    static FCDECL2 (FC_BOOL_RET, StaticAnyWritersSince, CRWLock *pRWLock, DWORD dwSeqNum);
private:
    static void StaticAcquireReaderLock(CRWLock **ppRWLock, DWORD dwDesiredTimeout);
    static void StaticAcquireWriterLock(CRWLock **ppRWLock, DWORD dwDesiredTimeout);
    static void StaticReleaseReaderLock(CRWLock **ppRWLock);
    static void StaticReleaseWriterLock(CRWLock **ppRWLock);
    static void StaticRecoverLock(CRWLock **ppRWLock, LockCookie *pLockCookie, DWORD dwFlags);
    static void StaticRestoreLock(CRWLock **ppRWLock, LockCookie *pLockCookie);
    static void StaticUpgradeToWriterLock(CRWLock **ppRWLock, LockCookie *pLockCookie, DWORD dwDesiredTimeout);
public:
    // Assert functions
#ifdef _DEBUG
    BOOL AssertWriterLockHeld();
    BOOL AssertWriterLockNotHeld();
    BOOL AssertReaderLockHeld();
    BOOL AssertReaderLockNotHeld();
    BOOL AssertReaderOrWriterLockHeld();
    void AssertHeld()
    { 
        WRAPPER_NO_CONTRACT; 
        AssertWriterLockHeld(); 
    }
    void AssertNotHeld()
    { 
        WRAPPER_NO_CONTRACT;
        AssertWriterLockNotHeld();
        AssertReaderLockNotHeld(); 
    }
#else
    void AssertWriterLockHeld()                  { LIMITED_METHOD_CONTRACT; }
    void AssertWriterLockNotHeld()               { LIMITED_METHOD_CONTRACT; }
    void AssertReaderLockHeld()                  { LIMITED_METHOD_CONTRACT; }
    void AssertReaderLockNotHeld()               { LIMITED_METHOD_CONTRACT; }
    void AssertReaderOrWriterLockHeld()          { LIMITED_METHOD_CONTRACT; }
    void AssertHeld()                            { LIMITED_METHOD_CONTRACT; }
    void AssertNotHeld()                         { LIMITED_METHOD_CONTRACT; }
#endif

    // Helper functions
#ifdef RWLOCK_STATISTICS
    DWORD GetReaderEntryCount()                  
    { 
        LIMITED_METHOD_CONTRACT;
        return(_dwReaderEntryCount); 
    }
    DWORD GetReaderContentionCount()             { LIMITED_METHOD_CONTRACT; return(_dwReaderContentionCount); }
    DWORD GetWriterEntryCount()                  { LIMITED_METHOD_CONTRACT; return(_dwWriterEntryCount); }
    DWORD GetWriterContentionCount()             { LIMITED_METHOD_CONTRACT; return(_dwWriterContentionCount); }
#endif
    // Static functions
    static void *operator new(size_t size)       
    { 
        CONTRACTL
        {
            THROWS;
            GC_NOTRIGGER;
        }
        CONTRACTL_END;

        return ::operator new(size); 
    }
    static void ProcessInit();

    static void SetTimeout(DWORD dwTimeout)      
    { 
        LIMITED_METHOD_CONTRACT;

        gdwDefaultTimeout = dwTimeout; 
    }
    static DWORD GetTimeout()                    
    { 
        LIMITED_METHOD_CONTRACT; 
        return(gdwDefaultTimeout); 
    }
    static void SetSpinCount(DWORD dwSpinCount)  
    { 
        LIMITED_METHOD_CONTRACT;

        gdwDefaultSpinCount = g_SystemInfo.dwNumberOfProcessors > 1
                                                    ? dwSpinCount
                                                    : 0; 
    }
    static DWORD GetSpinCount()                  { LIMITED_METHOD_CONTRACT; return(gdwDefaultSpinCount); }

private:
    // Private helpers
    static void ChainEntry(Thread *pThread, LockEntry *pLockEntry);
    LockEntry *GetLockEntry(Thread *pThread = NULL);
    LockEntry *FastGetOrCreateLockEntry();
    LockEntry *SlowGetOrCreateLockEntry(Thread *pThread);
    void FastRecycleLockEntry(LockEntry *pLockEntry);
    static void RecycleLockEntry(LockEntry *pLockEntry);

    CLREvent* GetReaderEvent(HRESULT *pHR);
    CLREvent* GetWriterEvent(HRESULT *pHR);
    void ReleaseEvents();

    static LONG RWInterlockedCompareExchange(LONG RAW_KEYWORD(volatile) *pvDestination,
                                              LONG dwExchange,
                                              LONG dwComperand);
    static LONGLONG RWInterlockedCompareExchange64(LONGLONG RAW_KEYWORD(volatile) *pvDestination,
                                                   LONGLONG qwExchange,
                                                   LONGLONG qwComparand);
    static void* RWInterlockedCompareExchangePointer(PVOID RAW_KEYWORD(volatile) *pvDestination,
                                                   PVOID pExchange,
                                                   PVOID pComparand);
    static LONG RWInterlockedExchangeAdd(LONG RAW_KEYWORD(volatile) *pvDestination, LONG dwAddState);
    static LONG RWInterlockedIncrement(LONG RAW_KEYWORD(volatile) *pdwState);

    static DWORD RWWaitForSingleObject(CLREvent* event, DWORD dwTimeout);
    static void RWSetEvent(CLREvent* event);
    static void RWResetEvent(CLREvent* event);
    static void RWSleep(DWORD dwTime);

#if defined(ENABLE_CONTRACTS_IMPL)
    // The LOCK_TAKEN/RELEASED macros need a "pointer" to the lock object to do
    // comparisons between takes & releases (and to provide debugging info to the
    // developer).  We can't use "this" (*ppRWLock), because CRWLock is an Object and thus
    // can move.  So we use _dwLLockID instead.  It's not exactly unique, but it's
    // good enough--worst that can happen is if a thread takes RWLock A and erroneously
    // releases RWLock B (instead of A), we'll fail to catch that if their _dwLLockID's
    // are the same.  On 64 bits, we can use both _dwULockID & _dwLLockID and be unique
    static void * GetPtrForLockContract(CRWLock ** ppRWLock)
    {
#if defined(_WIN64)
        return (void *)
            (
                (
                    ((__int64) ((*ppRWLock)->_dwULockID)) << 32
                )
                |
                (
                    (__int64) ((*ppRWLock)->_dwLLockID)
                )
            );
#else //defined(_WIN64)
            return LongToPtr((*ppRWLock)->_dwLLockID);
#endif //defined(_WIN64)
    }
#endif //defined(ENABLE_CONTRACTS_IMPL)

    // private new
    static void *operator new(size_t size, void *pv)   { LIMITED_METHOD_CONTRACT;  return(pv); }

    // Private data
    CLREvent *_hWriterEvent;
    CLREvent *_hReaderEvent;
    OBJECTHANDLE _hObjectHandle;
    Volatile<LONG> _dwState;
    LONG _dwULockID;
    LONG _dwLLockID;
    DWORD _dwWriterID;
    DWORD _dwWriterSeqNum;
    WORD _wWriterLevel;
#ifdef RWLOCK_STATISTICS
    // WARNING: You must explicitly #define RWLOCK_STATISTICS when you build
    // in both the VM and BCL directories, as the managed class must also 
    // contain these fields!
    Volatile<LONG> _dwReaderEntryCount;
    Volatile<LONG> _dwReaderContentionCount;
    Volatile<LONG> _dwWriterEntryCount;
    Volatile<LONG> _dwWriterContentionCount;
    Volatile<LONG> _dwEventsReleasedCount;
#endif

    // Static data
    static Volatile<LONGLONG> s_mostRecentLockID;
    static CrstStatic       s_RWLockCrst;
};

#ifdef USE_CHECKED_OBJECTREFS
typedef REF<CRWLock> RWLOCKREF;

#else
typedef CRWLock*     RWLOCKREF;
#endif

#endif // _RWLOCK_H_

#endif // FEATURE_RWLOCK