ImageResizer  3.4.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events
Public Member Functions | Protected Attributes | List of all members
ImageResizer.Plugins.DiskCache.LockProvider Class Reference

Provides locking based on a string key. Locks are local to the LockProvider instance. The class handles disposing of unused locks. Generally used for coordinating writes to files (of which there can be millions). Only keeps key/lock pairs in memory which are in use. Thread-safe. More...

Public Member Functions

bool MayBeLocked (string key)
 Returns true if the given key might be locked. More...
 
bool TryExecute (string key, int timeoutMs, LockCallback success)
 Attempts to execute the 'success' callback inside a lock based on 'key'. If successful, returns true. If the lock cannot be acquired within 'timoutMs', returns false In a worst-case scenario, it could take up to twice as long as 'timeoutMs' to return false. More...
 

Protected Attributes

Dictionary< String, Object > locks
 The only objects in this collection should be for open files. More...
 
object createLock = new object()
 Synchronization object for modifications to the 'locks' dictionary More...
 

Detailed Description

Provides locking based on a string key. Locks are local to the LockProvider instance. The class handles disposing of unused locks. Generally used for coordinating writes to files (of which there can be millions). Only keeps key/lock pairs in memory which are in use. Thread-safe.

Definition at line 18 of file LockProvider.cs.

Member Function Documentation

bool ImageResizer.Plugins.DiskCache.LockProvider.MayBeLocked ( string  key)
inline

Returns true if the given key might be locked.

Parameters
key
Returns

Definition at line 35 of file LockProvider.cs.

36  {
37  lock (createLock)
38  {
39  return locks.ContainsKey(key);
40  }
41  }
object createLock
Synchronization object for modifications to the &#39;locks&#39; dictionary
Definition: LockProvider.cs:28
bool ImageResizer.Plugins.DiskCache.LockProvider.TryExecute ( string  key,
int  timeoutMs,
LockCallback  success 
)
inline

Attempts to execute the 'success' callback inside a lock based on 'key'. If successful, returns true. If the lock cannot be acquired within 'timoutMs', returns false In a worst-case scenario, it could take up to twice as long as 'timeoutMs' to return false.

Parameters
key
success
failure
timeoutMs

Definition at line 53 of file LockProvider.cs.

53  {
54  //Record when we started. We don't want an infinite loop.
55  DateTime startedAt = DateTime.UtcNow;
56 
57  // Tracks whether the lock acquired is still correct
58  bool validLock = true;
59  // The lock corresponding to 'key'
60  object itemLock = null;
61 
62  try {
63  //We have to loop until we get a valid lock and it stays valid until we lock it.
64  do {
65  // 1) Creation/aquire phase
66  lock (createLock) {
67  // We have to lock on dictionary writes, since otherwise
68  // two locks for the same file could be created and assigned
69  // at the same time. (i.e, between TryGetValue and the assignment)
70  if (!locks.TryGetValue(key, out itemLock))
71  locks[key] = itemLock = new Object(); //make a new lock!
72 
73  }
74  // Loophole (part 1):
75  // Right here - this is where another thread (executing part 2) could remove 'itemLock'
76  // from the dictionary, and potentially, yet another thread could
77  // insert a new value for 'itemLock' into the dictionary... etc, etc..
78 
79  // 2) Execute phase
80  if (System.Threading.Monitor.TryEnter(itemLock, timeoutMs)) {
81  try {
82  // May take minutes to acquire this lock.
83 
84  // Trying to detect an occurence of loophole above
85  // Check that itemLock still exists and matches the dictionary
86  lock (createLock) {
87  object newLock = null;
88  validLock = locks.TryGetValue(key, out newLock);
89  validLock = validLock && newLock == itemLock;
90  }
91  // Only run the callback if the lock is valid
92  if (validLock) {
93  success(); // Extremely long-running callback, perhaps throwing exceptions
94  return true;
95  }
96 
97  } finally {
98  System.Threading.Monitor.Exit(itemLock);//release lock
99  }
100  } else {
101  validLock = false; //So the finally clause doesn't try to clean up the lock, someone else will do that.
102  return false; //Someone else had the lock, they can clean it up.
103  }
104 
105  //Are we out of time, still having an invalid lock?
106  if (!validLock && Math.Abs(DateTime.UtcNow.Subtract(startedAt).TotalMilliseconds) > timeoutMs) {
107  //We failed to get a valid lock in time.
108  return false;
109  }
110 
111 
112  // If we had an invalid lock, we have to try everything over again.
113  } while (!validLock);
114  } finally {
115  if (validLock) {
116  // Loophole (part 2). When loophole part 1 and 2 cross paths,
117  // An lock object may be removed before being used, and be orphaned
118 
119  // 3) Cleanup phase - Attempt cleanup of lock objects so we don't
120  // have a *very* large and slow dictionary.
121  lock (createLock) {
122  // TryEnter() fails instead of waiting.
123  // A normal lock would cause a deadlock with phase 2.
124  // Specifying a timeout would add great and pointless overhead.
125  // Whoever has the lock will clean it up also.
126  if (System.Threading.Monitor.TryEnter(itemLock)) {
127  try {
128  // It succeeds, so no-one else is working on it
129  // (but may be preparing to, see loophole)
130  // Only remove the lock object if it
131  // still exists in the dictionary as-is
132  object existingLock = null;
133  if (locks.TryGetValue(key, out existingLock)
134  && existingLock == itemLock)
135  locks.Remove(key);
136  } finally {
137  // Remove the lock
138  System.Threading.Monitor.Exit(itemLock);
139  }
140  }
141  }
142  }
143  }
144  // Ideally the only objects in 'locks' will be open operations now.
145  return true;
146  }
object createLock
Synchronization object for modifications to the &#39;locks&#39; dictionary
Definition: LockProvider.cs:28
Dictionary< String, Object > locks
The only objects in this collection should be for open files.
Definition: LockProvider.cs:23

Member Data Documentation

object ImageResizer.Plugins.DiskCache.LockProvider.createLock = new object()
protected

Synchronization object for modifications to the 'locks' dictionary

Definition at line 28 of file LockProvider.cs.

Dictionary<String, Object> ImageResizer.Plugins.DiskCache.LockProvider.locks
protected
Initial value:
=
new Dictionary<string, object>(StringComparer.Ordinal)

The only objects in this collection should be for open files.

Definition at line 23 of file LockProvider.cs.


The documentation for this class was generated from the following file: