136 lines
8.2 KiB
Markdown
136 lines
8.2 KiB
Markdown
---
|
||
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.* |