Skip to content

ZTS (thread-safe) support + persistent session pool #80

Description

@mroest

Hi!

Thank you for your work on the php-pkcs11 extension!

We are planning to deploy it in a FrankenPHP worker-mode setup where PHP threads are long-lived. Our performance requirements make two things necessary: a ZTS-compatible build, and a persistent session pool to avoid repeated C_OpenSession overhead against the HSM on every request.

Before investing more time in testing and polishing, we wanted to check whether you'd be interested in taking this upstream at all.

I've implemented the changes in a fork on the feature/php-zts branch: https://github.com/mroest/php-pkcs11/tree/feature/php-zts

What was added:

  1. ZTS support — the extension compiles cleanly with ZTS enabled. C_Initialize is called with CKF_OS_LOCKING_OK in threaded builds and NULL in non-ZTS builds, preserving existing behaviour.

  2. Process-wide library registry (pkcs11module.c) — a HashTable keyed by resolved library path, protected by a pthread_mutex, prevents duplicate C_Initialize calls across threads sharing the same HSM library.

  3. Per-thread session pool — a ZEND_MODULE_GLOBALS session pool (keyed by realpath:slotID:flags) amortises C_OpenSession cost across requests. Sessions are logged out before being returned to the pool, so every reuse requires a fresh login, avoiding credential leakage between requests.

  4. Stale-session resilience — a PKCS11_SESSION_CALL macro transparently evicts an invalid pool entry, opens a fresh session, and retries the operation once on stale-handle errors (CKR_SESSION_HANDLE_INVALID etc.).

  5. 12 new .phpt tests covering pool reuse, credential rejection, stale eviction, tainted-session cleanup, and shutdown ordering.

All changes are backward-compatible — non-ZTS builds follow the same code paths without the threading overhead.

Current status: tested against SoftHSM2 only. We plan to test against real hardware, but wanted to gauge your interest first before investing further.

Where its used: https://github.com/OpenConext/OpenConext-private-key-agent

Happy to discuss the approach in more detail. Let me know what you think!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions