-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #497 from wikimedia/counter
Uses a UNIX semaphore to limit concurrent ebook-convert calls
- Loading branch information
Showing
8 changed files
with
145 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace App\Util\Semaphore; | ||
|
||
/** | ||
* A semaphore aka a lock allowing multiple threads/processes to acquire it. | ||
*/ | ||
interface Semaphore { | ||
/** | ||
* Attempts to lock the semaphore, returns null if it can't be locked because the semaphore is full. | ||
*/ | ||
public function tryLock(): ?SemaphoreHandle; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
namespace App\Util\Semaphore; | ||
|
||
/** | ||
* A currently acquired semaphore | ||
*/ | ||
interface SemaphoreHandle { | ||
/** Releases the semaphore handle: allows another process to lock it. */ | ||
public function release(): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
namespace App\Util\Semaphore; | ||
|
||
use Exception; | ||
|
||
/** | ||
* A semaphore backed by POSIX file APIs. | ||
* Does not work on Windows. | ||
*/ | ||
class UnixSemaphore implements Semaphore { | ||
/** @var int */ | ||
private $semaphoreKey; | ||
/** @var int */ | ||
private $capacity; | ||
/** @var resource|null the lazily initialized semaphore descriptor */ | ||
private $semaphore; | ||
|
||
/** | ||
* @param int $semaphoreKey unique identifier for this semaphore. Should be shared by all the processes using it. | ||
* @param int $capacity how many processes can lock the same semaphore. | ||
*/ | ||
public function __construct( int $semaphoreKey, int $capacity ) { | ||
$this->semaphoreKey = $semaphoreKey; | ||
$this->capacity = $capacity; | ||
$this->semaphore = null; | ||
} | ||
|
||
public function tryLock(): ?SemaphoreHandle { | ||
if ( $this->semaphore === null ) { | ||
$semaphore = sem_get( $this->semaphoreKey, $this->capacity ); | ||
if ( $semaphore === false ) { | ||
throw new Exception( "Failed to create semaphore key $this->semaphoreKey" ); | ||
} | ||
$this->semaphore = $semaphore; | ||
} | ||
|
||
if ( !sem_acquire( $this->semaphore, true ) ) { | ||
return null; // Semaphore already full | ||
} | ||
|
||
// We return the handle | ||
return new UnixSemaphoreHandle( $this->semaphore ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace App\Util\Semaphore; | ||
|
||
class UnixSemaphoreHandle implements SemaphoreHandle { | ||
/** @var resource */ | ||
private $semaphore; | ||
/** @var bool */ | ||
private $isReleased; | ||
|
||
public function __construct( $semaphore ) { | ||
$this->semaphore = $semaphore; | ||
$this->isReleased = false; | ||
} | ||
|
||
public function release(): void { | ||
if ( $this->isReleased ) { | ||
return; // Already released | ||
} | ||
$this->isReleased = true; | ||
sem_release( $this->semaphore ); | ||
} | ||
|
||
public function __destruct() { | ||
$this->release(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
namespace App\Tests\Util\Semaphore; | ||
|
||
use App\Util\Semaphore\SemaphoreHandle; | ||
use App\Util\Semaphore\UnixSemaphore; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class UnixSemaphoreTest extends TestCase { | ||
|
||
/** | ||
* @covers \App\Util\Semaphore\UnixSemaphore | ||
* @covers \App\Util\Semaphore\UnixSemaphoreHandle | ||
*/ | ||
public function testUnixSemaphore() { | ||
$semaphore = new UnixSemaphore( 1, 1 ); | ||
$handle = $semaphore->tryLock(); | ||
$this->assertInstanceOf( SemaphoreHandle::class, $handle ); | ||
$this->assertNull( $semaphore->tryLock() ); | ||
$handle->release(); | ||
$this->assertInstanceOf( SemaphoreHandle::class, $semaphore->tryLock() ); | ||
} | ||
} |