Files
DP44/enriched-qwen3-coder-next/Common/DTS.Common/Classes/Locking.md

136 lines
8.2 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- Common/DTS.Common/Classes/Locking/LockRecord.cs
- Common/DTS.Common/Classes/Locking/LockError.cs
- Common/DTS.Common/Classes/Locking/SensorsLocking.cs
generated_at: "2026-04-16T03:16:01.173429+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "4c9a49fc0d53b85f"
---
# Locking
## Documentation: `DTS.Common.Classes.Locking` Module
---
### 1. Purpose
This module provides infrastructure for managing application-level locks on sensor-related data items within a distributed system. It defines core data structures (`LockRecord`, `LockError`) to represent lock state and error conditions, and a high-level locking coordinator (`SensorsLocking`) responsible for maintaining and validating locks on behalf of UI components (e.g., sensor editing controls). The module supports lock renewal, theft detection, and loss handling, integrating with logging (`APILogger`), event aggregation (`Prism.Events`), and a lower-level `LockManager` (referenced but not included here) for persistence operations.
---
### 2. Public Interface
#### `LockRecord` Class
Represents a single active lock on a data item.
- **Constructor**
```csharp
public LockRecord(string user, string machine, DateTime createTime, DateTime lastUpdate, string itemKey, int itemCategory, int itemId)
```
Initializes a new lock record with the specified metadata. All properties are read-only after construction.
- **Properties**
- `string LockingUserName` Name of the user who acquired the lock.
- `string LockingMachineName` Hostname of the machine holding the lock.
- `DateTime CreationTime` Timestamp when the lock was first acquired.
- `DateTime LastUpdated` Timestamp of the last lock renewal/maintenance.
- `string ItemKey` Human-readable key identifying the locked item (e.g., sensor name).
- `int ItemCategory` Numeric category of the item (e.g., `LockCategories` enum value in DB).
- `int ItemId` Numeric ID of the locked item (e.g., sensor ID).
#### `LockError` Class
Encapsulates error information from lock operations.
- **Constructor**
```csharp
public LockError(int error, string message, string user = null, string machine = null)
```
Creates an error instance with code, message, and optional lock holder details.
- **Properties**
- `int ErrorCode` Numeric error code (see constants below).
- `string Message` Human-readable description.
- `string LockingUser` User associated with the lock at time of error (empty if unknown).
- `string LockingMachine` Machine associated with the lock at time of error (empty if unknown).
- `bool LockStolen => ErrorCode == LOCKSTOLEN_ERROR` Indicates the lock was forcibly released by another user.
- `bool LockLost => ErrorCode == LOCKDOESNTEXIST_ERROR` Indicates the lock no longer exists in the backing store.
- **Constants**
- `BAD_NETPATH_ERROR = 994` Network path error (e.g., lost remote DB connection).
- `SEM_TIMEOUT_ERROR = 995` Semaphore timeout (e.g., connection failure).
- `ITEM_NOT_FOUND = 996` Referenced item not found.
- `LOCKDOESNTEXIST_ERROR = 997` Lock record missing (lock lost).
- `LOCKSTOLEN_ERROR = 998` Lock stolen by another user.
- `UNKNOWN_ERROR = 999` Generic/unspecified error.
#### `SensorsLocking` Class
Manages lock lifecycle for sensor UI controls.
- **`Unlock(bool bCheckLock, string userName, int userId)`**
Stops background lock renewal and attempts to release all tracked locks via `FreeLocks()`. Logs exceptions but does not propagate them.
*Note: Parameter `bCheckLock` is unused in the current implementation.*
- **`IsCurrentlyLocked(LockRecord lockRecord)`**
```csharp
public bool IsCurrentlyLocked(LockRecord lockRecord)
```
Returns `true` if the provided `lockRecord` matches an item currently tracked by this instance (i.e., `ItemId` and `ItemCategory` match any entry in `_existingLocks`). Thread-safe via `MyLock`.
- **Private Methods (Referenced)**
- `StopUpdating()` Cancels background renewal task and waits up to `MAX_WAIT_TIME_MS` (10s) for completion. Returns `true` only if stopped cleanly.
- `FreeLocks()` Iterates over `_existingLocks` and calls `LockManager.FreeLoc` for each (implementation truncated in source).
- `CheckLocks(bool publishErrors, string userName, int userId)` Validates all tracked locks; on failure, clears `_existingLocks`, calls `WipeOutChanges()`, and publishes `PageErrorEvent` if `publishErrors` is `true`.
- `CheckLock(...)` *Not implemented* (`NotImplementedException`).
- `WipeOutChanges()` *Not implemented* (`NotImplementedException`).
- `GetErrorMessage(LockError[] errors, LockRecord[] lockRecords)` Builds a localized error message string for display, handling `LockStolen` and `LockLost` cases.
---
### 3. Invariants
- `_existingLocks` is always `null` or a non-null array (never partially initialized).
- Lock records in `_existingLocks` are only modified or cleared under the `MyLock` synchronization object.
- `LockRecord` instances are immutable after construction (all properties are `readonly` or get-only).
- `LockError.ErrorCode` is set once at construction and never modified.
- Lock renewal/maintenance is performed periodically via `_updateTask`; failure to renew triggers `CheckLocks()` failure.
- On *any* lock failure during `CheckLocks()`, `_existingLocks` is atomically reset to an empty array, and `WipeOutChanges()` is called (though implementation is missing).
- `LockManager.FreeLoc` is assumed to be the canonical method for releasing locks (external dependency).
---
### 4. Dependencies
#### **Internal Dependencies (from imports)**
- `DTS.Common.Events` For `PageErrorEvent` and `PageErrorArg`.
- `DTS.Common.SharedResource.Strings` For localized strings (`StringResources.FailedToUpdateLock`, `LockStolen_Sensors`, `LockLost_Sensors`).
- `DTS.Common.Utilities.Logging` For `APILogger`.
- `Prism.Events` For `IEventAggregator` and `ContainerLocator.Container`.
- `System.Threading.Tasks` For `Task`, `CancellationTokenSource`.
#### **External Dependencies (inferred)**
- `LockManager` Referenced in `FreeLocks()` but not defined in the provided source. Critical dependency for lock acquisition/release.
- `PageErrorEvent` A Prism event used to publish UI-level errors.
- `ContainerLocator.Container` Prism/IoC container for resolving services (e.g., `IEventAggregator`).
#### **Depended Upon By**
- UI controls (e.g., sensor editing forms) that need to track and release locks on sensor items.
- Likely used in conjunction with `LockManager` for end-to-end locking workflows.
---
### 5. Gotchas
- **`CheckLock` and `WipeOutChanges` are unimplemented** (`NotImplementedException`). This module is incomplete and non-functional as-is.
- **`bCheckLock` parameter in `Unlock()` is ignored** The method always proceeds to `FreeLocks()` regardless of its value.
- **`FreeLocks()` calls `LockManager.FreeLoc` without arguments** The source is truncated (`FreeLoc` call is cut off), so the exact signature and required parameters (e.g., `LockRecord`) are unknown.
- **`GetErrorMessage` uses `Array.Exists` with `ItemId`/`ItemCategory` but `IsCurrentlyLocked` does the same** Redundant logic suggests potential for refactoring or inconsistency if `LockRecord` equality semantics change.
- **No validation of `LockRecord` constructor parameters** No null checks on `user`, `machine`, or `itemKey`; invalid values (e.g., empty `ItemKey`) may propagate silently.
- **`LockingUser`/`LockingMachine` in `LockError` default to `string.Empty`** Not `null`, which simplifies UI display but may mask missing data.
- **`MAX_WAIT_TIME_MS = 10000` is hardcoded** No configuration or override mechanism for timeout duration.
- **Thread-safety relies on `MyLock` but is inconsistent** `IsCurrentlyLocked` uses `lock(MyLock)`, but `CheckLocks` and `FreeLocks` use `lock(MyLock)` only for reading `_existingLocks`; background task (`_updateTask`) state is managed separately.
- **No explicit lock acquisition methods** The module only handles *renewal* and *release* of *already-acquired* locks (via `LockManager`), implying lock acquisition happens elsewhere.
*None identified beyond the above.*