--- 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.*