> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/core/basic-operations/lock.md).

# Lock a PDF document on Server/Desktop

Learn how to manage PDF document instances across multiple threads with Apryse SDK locking mechanisms. Ensure stability and avoid crashes by implementing proper document locking. Access full source co

{% hint style="info" %}
If you plan on sharing a single PDF document instance across multiple threads, then you need to lock the document. Not locking a PDF document instance shared across threads can result in undefined behaviour, including crashing.
{% endhint %}

An Apryse SDK lock is based on two principles:

[Reentrant mutex (also known as recursive lock)](https://en.wikipedia.org/wiki/Reentrant_mutex) Recursive mutex may be locked multiple times by the same process/thread without causing a deadlock.

[Readers-writer (RW) lock (also known as shared-exclusive lock)](https://en.wikipedia.org/wiki/Readers-writer_lock) An RW lock allows concurrent access for read-only operations, while write operations require exclusive access.

## Write to a document

To write to a PDF document in a multithreaded environment.

{% hint style="warning" %}
Only one thread can hold a write lock at any given time.
{% endhint %}

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
PDFDoc doc = new PDFDoc(filename);

// Lock the document
doc.Lock();

// ... perform some document processing using write operations

// Now we release the lock
doc.Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
PDFDoc doc(filename);

// Lock the document
doc.Lock();

// ... perform some document processing using write operations

// Now we release the lock
doc.Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
doc := NewPDFDoc(filename)

// Lock the document
doc.Lock()

// ... perform some document processing using write operations

// Now we release the lock
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
PDFDoc doc = new PDFDoc(filename);

// Lock the document
doc.lock();

// ... perform some document processing using write operations

// Now we release the lock
doc.unlock();
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Lock the document
  doc.lock();

  // ... perform some document processing using write operations

  // Now we release the lock
  doc.unlock();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
val doc = PDFDoc(filename)

// Lock the document
doc.lock()

// ... perform some document processing using write operations

// Now we release the lock
doc.unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
PTPDFDoc doc = [[PTPDFDoc alloc] initWithFilePath:filename];

// Lock the document
[doc Lock];

// ... perform some document processing using write operations

// Now we release the lock
[doc Unlock];
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
let doc: PTPDFDoc = PTPDFDoc(filename)

// Lock the document
doc.lock()

// ... perform some document processing using write operations

// Now we release the lock
doc.unlock()
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$doc = new PDFDoc($filename);

// Lock the document
$doc->Lock();

// ... perform some document processing using write operations

// Now we release the lock
$doc->Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
doc = PDFDoc(filename)

# Lock the document
doc.Lock()

# ... perform some document processing using write operations

# Now we release the lock
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
doc = PDFDoc.new(filename)

# Lock the document
doc.Lock()

# ... perform some document processing using write operations

# Now we release the lock
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="VB" %}
{% code lineNumbers="true" %}

```vb
Dim doc as PDFDoc = New PDFDoc(filename)

' Lock the document
doc.Lock()

' ... perform some document processing using write operations

' Now we release the lock
doc.Unlock()
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Multithreaded PDF reading](/core/get-started/samples/multithreadedtest.md) Full source code which illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model. Code sample is available in C#.

## Read and write to a document

To read and write to a PDF document in a multithreaded environment.

{% hint style="warning" %}
A thread cannot acquire a write lock while holding a read lock.
{% endhint %}

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
PDFDoc doc = new PDFDoc(filename);

// Lock the document, since we are going to write to this thread.
doc.Lock();

// Optionally note that locking a second time does not cause a deadlock
doc.Lock();

// You can acquire a read lock while holding a write lock:
doc.LockRead();

// ... perform some document processing using read operations

doc.UnlockRead();

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.Unlock();

// Now we release the write lock, and
// other tasks can begin execution
doc.Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
PDFDoc doc(filename);

// Lock the document, since we are going to write to this thread.
doc.Lock();

// Optionally note that locking a second time does not cause a deadlock
doc.Lock();

// You can acquire a read lock while holding a write lock:
doc.LockRead();

// ... perform some document processing using read operations

doc.UnlockRead();

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.Unlock();

// Now we release the write lock, and
// other tasks can begin execution
doc.Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
doc := NewPDFDoc(filename)

// Lock the document, since we are going to write to this thread.
doc.Lock()

// Optionally note that locking a second time does not cause a deadlock
doc.Lock()

// You can acquire a read lock while holding a write lock:
doc.LockRead()

// ... perform some document processing using read operations

doc.UnlockRead()

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.Unlock()

// Now we release the write lock, and
// other tasks can begin execution
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
PDFDoc doc = new PDFDoc(filename);

// Lock the document
doc.lock();

// Optionally note that locking a second time does not cause a deadlock
doc.lock();

// You can acquire a read lock while holding a write lock:
doc.lockRead();

// ... perform some document processing using read operations

doc.unlockRead();

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.unlock();

// Now we release the write lock, and
// other tasks can begin execution
doc.unlock();
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Lock the document
  doc.lock();

  // Optionally note that locking a second time does not cause a deadlock
  doc.lock();

  // You can acquire a read lock while holding a write lock:
  doc.lockRead();

  // ... perform some document processing using read operations

  doc.unlockRead();

  // ... perform some document processing using write operations

  // Releasing the lock decrements our lock count,
  // but the thread still holds a write lock on the document
  doc.unlock();

  // Now we release the write lock, and
  // other tasks can begin execution
  doc.unlock();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
val doc = PDFDoc(filename)

// Lock the document
doc.lock()

// Optionally note that locking a second time does not cause a deadlock
doc.lock()

// You can acquire a read lock while holding a write lock:
doc.lockRead()

// ... perform some document processing using read operations

doc.unlockRead()

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.unlock()

// Now we release the write lock, and
// other tasks can begin execution
doc.unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
PTPDFDoc doc = [[PTPDFDoc alloc] initWithFilePath:filename];

// Lock the document
[doc Lock];

// Optionally note that locking a second time does not cause a deadlock
[doc Lock];

// You can acquire a read lock while holding a write lock:
[doc LockRead];

// ... perform some document processing using read operations

[doc Unlock];

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
[doc Unlock];

// Now we release the write lock, and
// other tasks can begin execution
[doc Unlock];
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
let doc: PTPDFDoc = PTPDFDoc(filename)

// Lock the document, since we are going to write to this thread.
doc.lock()

// Optionally note that locking a second time does not cause a deadlock
doc.lock()

// You can acquire a read lock while holding a write lock:
doc.lockRead()

// ... perform some document processing using read operations

doc.unlockRead()

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
doc.unlock()

// Now we release the write lock, and
// other tasks can begin execution
doc.unlock()
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$doc = new PDFDoc($filename);

// Lock the document
$doc->Lock();

// Optionally note that locking a second time does not cause a deadlock
$doc->Lock();

// You can acquire a read lock while holding a write lock:
$doc->LockRead();

// ... perform some document processing using read operations

$doc->UnlockRead();

// ... perform some document processing using write operations

// Releasing the lock decrements our lock count,
// but the thread still holds a write lock on the document
$doc->Unlock();

// Now we release the write lock, and
// other tasks can begin execution
$doc->Unlock();
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
doc = PDFDoc(filename)

# Lock the document, since we are going to write to this thread.
doc.Lock()

# Optionally note that locking a second time does not cause a deadlock
doc.Lock()

# You can acquire a read lock while holding a write lock:
doc.LockRead()

# ... perform some document processing using read operations

doc.UnlockRead()

# ... perform some document processing using write operations

# Releasing the lock decrements our lock count,
# but the thread still holds a write lock on the document
doc.Unlock()

# Now we release the write lock, and
# other tasks can begin execution
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
doc = PDFDoc.new(filename)

# Lock the document, since we are going to write to this thread.
doc.Lock()

# Optionally note that locking a second time does not cause a deadlock
doc.Lock()

# You can acquire a read lock while holding a write lock:
doc.LockRead()

# ... perform some document processing using read operations

doc.UnlockRead()

# ... perform some document processing using write operations

# Releasing the lock decrements our lock count,
# but the thread still holds a write lock on the document
doc.Unlock()

# Now we release the write lock, and
# other tasks can begin execution
doc.Unlock()
```

{% endcode %}
{% endtab %}

{% tab title="VB" %}
{% code lineNumbers="true" %}

```vb
Dim doc as PDFDoc = New PDFDoc(filename)

' Lock the document, since we are going to write to this thread.
doc.Lock()

' Optionally note that locking a second time does not cause a deadlock
doc.Lock()

' You can acquire a read lock while holding a write lock:
doc.LockRead()

' ... perform some document processing using read operations

doc.UnlockRead()

' ... perform some document processing using write operations

' Releasing the lock decrements our lock count,
' but the thread still holds a write lock on the document
doc.Unlock()

' Now we release the write lock, and
' other tasks can begin execution
doc.Unlock()
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Multithreaded PDF reading](/core/get-started/samples/multithreadedtest.md) Full source code which illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model. Code sample is available in C#.

## About locking a document

As computing devices become more parallel in nature, Apryse is evolving to allow developers to leverage this power in new and exciting ways. Apryse version 6.0 introduces new locking semantics which allow for concurrent access of a `PDFDoc` instance. This was done to improve performance during interactive viewing (simultaneous rendering, text extraction, etc.), as well as to open up the possibility for new use cases (parallel rendering). This article introduces the locking system, and will get you on your way to developing parallel applications with Apryse SDK.

## PDFDoc lock

Apryse uses a recursive read/write locking system. Multiple threads can hold a read lock on the document, **but only one thread can hold a write lock at any given time**. A thread can acquire an equivalent or weaker lock as many times as it likes without causing a deadlock. In other words, the following is valid:

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
PDFDoc d = new PDFDoc("foo.pdf");
d.Lock();
d.Lock();
d.LockRead();
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
PDFDoc d = new PDFDoc("foo.pdf");
d.Lock();
d.Lock();
d.LockRead();
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
d := NewPDFDoc(filename)
d.Lock();
d.Lock();
d.LockRead();
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
PDFDoc d = new PDFDoc("foo.pdf");
d.lock();
d.lock();
d.lockRead();
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL("foo.pdf");
  d.lock();
  d.lock();
  d.lockRead();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
val d = PDFDoc("foo.pdf")
d.lock()
d.lock()
d.lockRead()
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
PTPDFDoc *doc = [[PTPDFDoc alloc] initWithFilename: @"foo.pdf"];
[d Lock];
[d Unlock];
[d LockRead];
[d UnlockRead];
```

{% endcode %}
{% endtab %}

{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
let d: PTPDFDoc = PDFDoc("foo.pdf")
d.lock()
d.lock()
d.lockRead()
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$d = new PDFDoc("foo.pdf");
$d.Lock();
$d.Lock();
$d.LockRead();
```

{% endcode %}
{% endtab %}
{% endtabs %}

However, a **thread cannot acquire a write lock while holding a read lock**. Because one can only acquire a write lock when no read locks are held, this situation would inevitably lead to a deadlock. To avoid this scenario, Apryse will throw a runtime exception whenever the situation occurs.

## Locking in the Apryse API

Some of our API calls internally acquire a write lock on the document. As a result, these calls can also throw a runtime exception if they are invoked while holding a read lock. This is noted in each method's documentation. Additionally, you will find a complete list of those methods at the end of this article.

In general, the parts of our library that manage the UI will maintain document locks. **For the lower level calls which actually modify the document, you are responsible for maintaining document locks.**

## Client side locking

Apryse provides the following APIs for locking the document:

## Migration of earlier code

The new locking system is backwards compatible, meaning previous calls to `PDFDoc.Lock` now acquire a write lock. If you would like to take advantage of the ability to read a PDF concurrently, it is your responsibility to review document locks and determine whether it is safe to downgrade them to a read lock.

## Opting out

Conversely, if you are happy with the existing *'one document, one thread'* model previously used in Apryse 5.9., you can continue to work with this system. No change is required on your behalf.

## View interrupt with DocLock

For convenience, `PDFView` or `PDFViewCtrl` exposes similar methods, which will be applied to the currently open document or associated document. Additionally, the `PDFView.DocLock` or `PDFViewCtrl.DocLock` method takes a `cancel_rendering` parameter, which will interrupt all worker threads currently accessing the document. This allows you to acquire the write lock as fast as possible:

## Input Filters

At the low level, a `PDFDoc` uses an `input filter` to access its PDF data. this data could be stored on the file system, in a memory buffer, or over a network. Now that Apryse supports concurrent access of `PDFDoc` across many threads; these input filters must also be made thread-safe. StdFile, which was not a thread-safe filter, is no longer available. Instead, you should now use the new `MappedFile` filter, which provides thread-safe and efficient read access on a file. Custom user filters are still supported, although they are now wrapped in an internal filter that guarantees thread safety.

## API calls which can acquire a write lock


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/core/basic-operations/lock.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
