or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

atomic-arrays.mdatomic-operations.mdindex.mdlocks.mdthread-parking.mdtracing.md

locks.mddocs/

0

# Locks and Synchronization

1

2

Multiplatform synchronization primitives including reentrant locks and synchronized blocks, providing thread-safe coordination mechanisms for complex concurrent operations.

3

4

## Capabilities

5

6

### Reentrant Lock Factory

7

8

Creates reentrant locks for advanced synchronization scenarios requiring explicit lock management.

9

10

```kotlin { .api }

11

/**

12

* Creates a new ReentrantLock instance.

13

* On JVM, this is a typealias for java.util.concurrent.locks.ReentrantLock.

14

* @returns New ReentrantLock instance

15

*/

16

fun reentrantLock(): ReentrantLock

17

18

/**

19

* JVM typealias for java.util.concurrent.locks.ReentrantLock

20

*/

21

typealias ReentrantLock = java.util.concurrent.locks.ReentrantLock

22

```

23

24

**Usage Examples:**

25

26

```kotlin

27

import kotlinx.atomicfu.locks.*

28

29

class ConcurrentResourceManager {

30

private val lock = reentrantLock()

31

private val resources = mutableMapOf<String, Any>()

32

33

fun addResource(key: String, resource: Any) {

34

lock.lock()

35

try {

36

resources[key] = resource

37

println("Added resource: $key")

38

} finally {

39

lock.unlock()

40

}

41

}

42

43

fun getResource(key: String): Any? {

44

lock.lock()

45

try {

46

return resources[key]

47

} finally {

48

lock.unlock()

49

}

50

}

51

52

fun removeResource(key: String): Any? {

53

lock.lock()

54

try {

55

return resources.remove(key)

56

} finally {

57

lock.unlock()

58

}

59

}

60

61

fun getAllKeys(): Set<String> {

62

lock.lock()

63

try {

64

return resources.keys.toSet()

65

} finally {

66

lock.unlock()

67

}

68

}

69

}

70

```

71

72

### ReentrantLock with Lock Extension

73

74

Convenient extension function for automatic lock management with proper cleanup.

75

76

```kotlin { .api }

77

/**

78

* Executes the given block while holding the lock, automatically releasing it afterwards.

79

* @param block - Code block to execute while holding the lock

80

* @returns Result of the block execution

81

*/

82

fun <T> ReentrantLock.withLock(block: () -> T): T

83

```

84

85

**Usage Examples:**

86

87

```kotlin

88

import kotlinx.atomicfu.locks.*

89

90

class ThreadSafeCounter {

91

private val lock = reentrantLock()

92

private var count = 0

93

94

fun increment(): Int = lock.withLock {

95

count++

96

count

97

}

98

99

fun decrement(): Int = lock.withLock {

100

count--

101

count

102

}

103

104

fun get(): Int = lock.withLock { count }

105

106

fun addAndGet(delta: Int): Int = lock.withLock {

107

count += delta

108

count

109

}

110

111

fun reset(): Int = lock.withLock {

112

val oldValue = count

113

count = 0

114

oldValue

115

}

116

}

117

118

// Complex example with multiple operations

119

class BankAccount(private val initialBalance: Double) {

120

private val lock = reentrantLock()

121

private var balance = initialBalance

122

private val transactions = mutableListOf<String>()

123

124

fun deposit(amount: Double): Double = lock.withLock {

125

require(amount > 0) { "Deposit amount must be positive" }

126

balance += amount

127

transactions.add("Deposit: +$amount")

128

balance

129

}

130

131

fun withdraw(amount: Double): Double = lock.withLock {

132

require(amount > 0) { "Withdrawal amount must be positive" }

133

require(balance >= amount) { "Insufficient funds" }

134

balance -= amount

135

transactions.add("Withdrawal: -$amount")

136

balance

137

}

138

139

fun getBalance(): Double = lock.withLock { balance }

140

141

fun getTransactionHistory(): List<String> = lock.withLock {

142

transactions.toList() // Return defensive copy

143

}

144

145

fun transfer(other: BankAccount, amount: Double): Boolean {

146

// Avoid deadlock by ordering locks by object hash

147

val (firstLock, secondLock) = if (this.hashCode() < other.hashCode()) {

148

this.lock to other.lock

149

} else {

150

other.lock to this.lock

151

}

152

153

return firstLock.withLock {

154

secondLock.withLock {

155

if (this.balance >= amount) {

156

this.balance -= amount

157

other.balance += amount

158

this.transactions.add("Transfer out: -$amount")

159

other.transactions.add("Transfer in: +$amount")

160

true

161

} else {

162

false

163

}

164

}

165

}

166

}

167

}

168

```

169

170

### Synchronized Object

171

172

Platform-specific synchronization object for use with synchronized blocks.

173

174

```kotlin { .api }

175

/**

176

* JVM typealias for Any - any object can be used for synchronization

177

*/

178

typealias SynchronizedObject = Any

179

180

/**

181

* Executes the given block synchronized on the specified lock object.

182

* @param lock - Object to synchronize on

183

* @param block - Code block to execute synchronized

184

* @returns Result of the block execution

185

*/

186

fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T

187

```

188

189

**Usage Examples:**

190

191

```kotlin

192

import kotlinx.atomicfu.locks.*

193

194

class SynchronizedDataStore {

195

private val lock = Any() // SynchronizedObject

196

private val data = mutableMapOf<String, String>()

197

198

fun put(key: String, value: String) = synchronized(lock) {

199

data[key] = value

200

}

201

202

fun get(key: String): String? = synchronized(lock) {

203

data[key]

204

}

205

206

fun remove(key: String): String? = synchronized(lock) {

207

data.remove(key)

208

}

209

210

fun size(): Int = synchronized(lock) {

211

data.size

212

}

213

214

fun getAllEntries(): Map<String, String> = synchronized(lock) {

215

data.toMap() // Return defensive copy

216

}

217

218

fun clear() = synchronized(lock) {

219

data.clear()

220

}

221

}

222

223

// Multiple synchronized objects for fine-grained locking

224

class MultiLockCache {

225

private val readLock = Any()

226

private val writeLock = Any()

227

private val cache = mutableMapOf<String, Any>()

228

private var cacheStats = mutableMapOf<String, Int>()

229

230

fun get(key: String): Any? = synchronized(readLock) {

231

val value = cache[key]

232

if (value != null) {

233

synchronized(writeLock) {

234

cacheStats[key] = (cacheStats[key] ?: 0) + 1

235

}

236

}

237

value

238

}

239

240

fun put(key: String, value: Any) = synchronized(writeLock) {

241

cache[key] = value

242

cacheStats[key] = cacheStats[key] ?: 0

243

}

244

245

fun getStats(): Map<String, Int> = synchronized(writeLock) {

246

cacheStats.toMap()

247

}

248

}

249

```

250

251

### ReentrantLock Core Operations

252

253

Direct access to ReentrantLock methods for advanced use cases.

254

255

```kotlin { .api }

256

class ReentrantLock {

257

/** Acquires the lock, blocking if necessary */

258

fun lock()

259

260

/** Attempts to acquire the lock without blocking */

261

fun tryLock(): Boolean

262

263

/** Releases the lock */

264

fun unlock()

265

}

266

```

267

268

**Usage Examples:**

269

270

```kotlin

271

import kotlinx.atomicfu.locks.*

272

import java.util.concurrent.TimeUnit

273

274

class AdvancedLockExample {

275

private val lock = reentrantLock()

276

private var sharedResource: String? = null

277

278

fun tryUpdateResource(newValue: String, timeoutMs: Long): Boolean {

279

val acquired = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)

280

if (!acquired) {

281

println("Failed to acquire lock within timeout")

282

return false

283

}

284

285

try {

286

// Simulate some work

287

Thread.sleep(100)

288

sharedResource = newValue

289

println("Resource updated to: $newValue")

290

return true

291

} finally {

292

lock.unlock()

293

}

294

}

295

296

fun getResourceWithTimeout(timeoutMs: Long): String? {

297

val acquired = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)

298

if (!acquired) {

299

println("Failed to acquire lock for reading")

300

return null

301

}

302

303

try {

304

return sharedResource

305

} finally {

306

lock.unlock()

307

}

308

}

309

310

fun forceUpdateResource(newValue: String) {

311

lock.lock() // Block until lock is available

312

try {

313

sharedResource = newValue

314

println("Resource force-updated to: $newValue")

315

} finally {

316

lock.unlock()

317

}

318

}

319

}

320

```

321

322

## Advanced Synchronization Patterns

323

324

### Reader-Writer Lock Pattern

325

326

Using multiple locks for reader-writer scenarios:

327

328

```kotlin

329

import kotlinx.atomicfu.locks.*

330

import kotlinx.atomicfu.*

331

332

class ReadWriteCache<K, V> {

333

private val readLock = reentrantLock()

334

private val writeLock = reentrantLock()

335

private val cache = mutableMapOf<K, V>()

336

private val readers = atomic(0)

337

338

fun read(key: K): V? {

339

readLock.withLock {

340

readers.incrementAndGet()

341

}

342

343

try {

344

return cache[key]

345

} finally {

346

readLock.withLock {

347

readers.decrementAndGet()

348

}

349

}

350

}

351

352

fun write(key: K, value: V) {

353

writeLock.withLock {

354

// Wait for all readers to finish

355

while (readers.value > 0) {

356

Thread.yield()

357

}

358

359

cache[key] = value

360

}

361

}

362

363

fun size(): Int = readLock.withLock { cache.size }

364

}

365

```

366

367

### Condition-based Synchronization

368

369

Using locks with condition variables:

370

371

```kotlin

372

import kotlinx.atomicfu.locks.*

373

import kotlinx.atomicfu.*

374

import java.util.concurrent.locks.Condition

375

376

class BlockingQueue<T>(private val capacity: Int) {

377

private val lock = reentrantLock()

378

private val notFull: Condition = lock.newCondition()

379

private val notEmpty: Condition = lock.newCondition()

380

private val queue = ArrayDeque<T>()

381

382

fun put(item: T) = lock.withLock {

383

while (queue.size >= capacity) {

384

notFull.await()

385

}

386

queue.addLast(item)

387

notEmpty.signal()

388

}

389

390

fun take(): T = lock.withLock {

391

while (queue.isEmpty()) {

392

notEmpty.await()

393

}

394

val item = queue.removeFirst()

395

notFull.signal()

396

item

397

}

398

399

fun size(): Int = lock.withLock { queue.size }

400

401

fun isEmpty(): Boolean = lock.withLock { queue.isEmpty() }

402

403

fun isFull(): Boolean = lock.withLock { queue.size >= capacity }

404

}

405

```

406

407

## Implementation Notes

408

409

### JVM Integration

410

411

- `ReentrantLock` is a direct typealias to `java.util.concurrent.locks.ReentrantLock`

412

- `SynchronizedObject` is a typealias to `Any` - any object can be used for synchronization

413

- `synchronized` function maps directly to Kotlin's `kotlin.synchronized`

414

- Full compatibility with existing Java concurrent programming patterns

415

416

### Performance Considerations

417

418

- ReentrantLocks have higher overhead than basic synchronization but offer more features

419

- `withLock` extension provides automatic cleanup and exception safety

420

- Synchronized blocks have lower overhead but less flexibility

421

- Choose based on specific requirements: simplicity vs. advanced features

422

423

### Best Practices

424

425

- Always use `withLock` or proper try/finally blocks for lock management

426

- Avoid holding locks for long periods

427

- Consider lock ordering to prevent deadlocks in multi-lock scenarios

428

- Use `tryLock` with timeouts for robust error handling

429

- Prefer atomic operations over locks when possible for better performance