@@ -82,12 +82,17 @@ private function fork(int $concurrency, \Closure $code): void
82
82
*
83
83
* @param \Closure(0|1): int $code The counter code
84
84
* @param \Closure(float): Mutex $mutexFactory
85
+ * @param \Closure(): void $setUp
85
86
*
86
87
* @dataProvider provideHighContentionCases
87
88
*/
88
89
#[DataProvider('provideHighContentionCases ' )]
89
- public function testHighContention (\Closure $ code , \Closure $ mutexFactory ): void
90
+ public function testHighContention (\Closure $ code , \Closure $ mutexFactory, ? \ Closure $ setUp = null ): void
90
91
{
92
+ if ($ setUp !== null ) {
93
+ $ setUp ();
94
+ }
95
+
91
96
$ concurrency = 10 ;
92
97
$ iterations = 1000 / $ concurrency ;
93
98
$ timeout = $ concurrency * 20 ;
@@ -110,14 +115,12 @@ public function testHighContention(\Closure $code, \Closure $mutexFactory): void
110
115
*/
111
116
public static function provideHighContentionCases (): iterable
112
117
{
113
- foreach (static ::provideExecutionIsSerializedWhenLockedCases () as [$ mutexFactory ]) {
118
+ foreach (static ::provideExecutionIsSerializedWhenLockedCases () as $ name => [$ mutexFactory ]) {
114
119
$ filename = tempnam (sys_get_temp_dir (), 'php-lock-high-contention ' );
115
120
116
121
static ::$ temporaryFiles [] = $ filename ;
117
122
118
- file_put_contents ($ filename , '0 ' );
119
-
120
- yield [
123
+ yield $ name => [
121
124
static function (int $ increment ) use ($ filename ): int {
122
125
$ counter = file_get_contents ($ filename );
123
126
$ counter += $ increment ;
@@ -127,21 +130,19 @@ static function (int $increment) use ($filename): int {
127
130
return $ counter ;
128
131
},
129
132
$ mutexFactory ,
133
+ static function () use ($ filename ): void {
134
+ file_put_contents ($ filename , '0 ' );
135
+ },
130
136
];
131
137
}
132
138
133
- $ addPDO = static function ($ dsn , $ user , $ password , $ vendor ) {
139
+ $ makePDOCase = static function (string $ dsn , string $ user , string $ password , string $ vendor ) {
134
140
$ pdo = self ::getPDO ($ dsn , $ user , $ password );
135
141
136
142
$ options = ['mysql ' => 'engine=InnoDB ' ];
137
143
$ option = $ options [$ vendor ] ?? '' ;
138
144
$ pdo ->exec ('CREATE TABLE IF NOT EXISTS counter(id INT PRIMARY KEY, counter INT) ' . $ option );
139
145
140
- $ pdo ->beginTransaction ();
141
- $ pdo ->exec ('DELETE FROM counter ' );
142
- $ pdo ->exec ('INSERT INTO counter VALUES (1, 0) ' );
143
- $ pdo ->commit ();
144
-
145
146
self ::$ pdo = null ;
146
147
147
148
return [
@@ -163,34 +164,40 @@ static function (int $increment) use ($dsn, $user, $password) {
163
164
164
165
return $ counter ;
165
166
},
166
- static function ($ timeout = 3 ) use ($ dsn , $ user , $ password ) {
167
+ static function ($ timeout ) use ($ dsn , $ user , $ password ) {
167
168
self ::$ pdo = null ;
168
169
$ pdo = self ::getPDO ($ dsn , $ user , $ password );
169
170
170
171
return new TransactionalMutex ($ pdo , $ timeout );
171
172
},
173
+ static function () use ($ pdo ): void {
174
+ $ pdo ->beginTransaction ();
175
+ $ pdo ->exec ('DELETE FROM counter ' );
176
+ $ pdo ->exec ('INSERT INTO counter VALUES (1, 0) ' );
177
+ $ pdo ->commit ();
178
+ },
172
179
];
173
180
};
174
181
175
182
if (getenv ('MYSQL_DSN ' )) {
176
183
$ dsn = getenv ('MYSQL_DSN ' );
177
184
$ user = getenv ('MYSQL_USER ' );
178
185
$ password = getenv ('MYSQL_PASSWORD ' );
179
- yield 'mysql ' => $ addPDO ($ dsn , $ user , $ password , 'mysql ' );
186
+ yield 'mysql ' => $ makePDOCase ($ dsn , $ user , $ password , 'mysql ' );
180
187
}
181
188
182
189
if (getenv ('PGSQL_DSN ' )) {
183
190
$ dsn = getenv ('PGSQL_DSN ' );
184
191
$ user = getenv ('PGSQL_USER ' );
185
192
$ password = getenv ('PGSQL_PASSWORD ' );
186
- yield 'postgres ' => $ addPDO ($ dsn , $ user , $ password , 'postgres ' );
193
+ yield 'postgres ' => $ makePDOCase ($ dsn , $ user , $ password , 'postgres ' );
187
194
}
188
195
}
189
196
190
197
/**
191
198
* Tests that five processes run sequentially.
192
199
*
193
- * @param \Closure(): Mutex $mutexFactory
200
+ * @param \Closure(float ): Mutex $mutexFactory
194
201
*
195
202
* @dataProvider provideExecutionIsSerializedWhenLockedCases
196
203
*/
@@ -200,7 +207,7 @@ public function testExecutionIsSerializedWhenLocked(\Closure $mutexFactory): voi
200
207
$ time = \microtime (true );
201
208
202
209
$ this ->fork (6 , static function () use ($ mutexFactory ): void {
203
- $ mutex = $ mutexFactory ();
210
+ $ mutex = $ mutexFactory (3 );
204
211
$ mutex ->synchronized (static function (): void {
205
212
\usleep (200 * 1000 );
206
213
});
@@ -221,29 +228,29 @@ public static function provideExecutionIsSerializedWhenLockedCases(): iterable
221
228
222
229
self ::$ temporaryFiles [] = $ filename ;
223
230
224
- yield 'flock ' => [static function ($ timeout = 3 ) use ($ filename ): Mutex {
231
+ yield 'flock ' => [static function ($ timeout ) use ($ filename ): Mutex {
225
232
$ file = fopen ($ filename , 'w ' );
226
233
227
- return new FlockMutex ($ file );
234
+ return new FlockMutex ($ file, $ timeout );
228
235
}];
229
236
230
- yield 'flockWithTimoutPcntl ' => [static function ($ timeout = 3 ) use ($ filename ): Mutex {
237
+ yield 'flockWithTimoutPcntl ' => [static function ($ timeout ) use ($ filename ): Mutex {
231
238
$ file = fopen ($ filename , 'w ' );
232
239
$ lock = Liberator::liberate (new FlockMutex ($ file , $ timeout ));
233
240
$ lock ->strategy = FlockMutex::STRATEGY_PCNTL ; // @phpstan-ignore property.notFound
234
241
235
242
return $ lock ->popsValue ();
236
243
}];
237
244
238
- yield 'flockWithTimoutBusy ' => [static function ($ timeout = 3 ) use ($ filename ): Mutex {
245
+ yield 'flockWithTimoutBusy ' => [static function ($ timeout ) use ($ filename ): Mutex {
239
246
$ file = fopen ($ filename , 'w ' );
240
247
$ lock = Liberator::liberate (new FlockMutex ($ file , $ timeout ));
241
248
$ lock ->strategy = FlockMutex::STRATEGY_BUSY ; // @phpstan-ignore property.notFound
242
249
243
250
return $ lock ->popsValue ();
244
251
}];
245
252
246
- yield 'semaphore ' => [static function ($ timeout = 3 ) use ($ filename ): Mutex {
253
+ yield 'semaphore ' => [static function () use ($ filename ): Mutex {
247
254
$ semaphore = sem_get (ftok ($ filename , 'b ' ));
248
255
self ::assertThat (
249
256
$ semaphore ,
@@ -257,7 +264,7 @@ public static function provideExecutionIsSerializedWhenLockedCases(): iterable
257
264
}];
258
265
259
266
if (getenv ('MEMCACHE_HOST ' )) {
260
- yield 'memcached ' => [static function ($ timeout = 3 ): Mutex {
267
+ yield 'memcached ' => [static function ($ timeout ): Mutex {
261
268
$ memcached = new \Memcached ();
262
269
$ memcached ->addServer (getenv ('MEMCACHE_HOST ' ), 11211 );
263
270
@@ -268,7 +275,7 @@ public static function provideExecutionIsSerializedWhenLockedCases(): iterable
268
275
if (getenv ('REDIS_URIS ' )) {
269
276
$ uris = explode (', ' , getenv ('REDIS_URIS ' ));
270
277
271
- yield 'PredisMutex ' => [static function ($ timeout = 3 ) use ($ uris ): Mutex {
278
+ yield 'PredisMutex ' => [static function ($ timeout ) use ($ uris ): Mutex {
272
279
$ clients = array_map (
273
280
static fn ($ uri ) => new Client ($ uri ),
274
281
$ uris
@@ -279,7 +286,7 @@ public static function provideExecutionIsSerializedWhenLockedCases(): iterable
279
286
280
287
if (class_exists (\Redis::class)) {
281
288
yield 'PHPRedisMutex ' => [
282
- static function ($ timeout = 3 ) use ($ uris ): Mutex {
289
+ static function ($ timeout ) use ($ uris ): Mutex {
283
290
$ apis = array_map (
284
291
static function (string $ uri ): \Redis {
285
292
$ redis = new \Redis ();
@@ -306,7 +313,7 @@ static function (string $uri): \Redis {
306
313
}
307
314
308
315
if (getenv ('MYSQL_DSN ' )) {
309
- yield 'MySQLMutex ' => [static function ($ timeout = 3 ): Mutex {
316
+ yield 'MySQLMutex ' => [static function ($ timeout ): Mutex {
310
317
$ pdo = new \PDO (getenv ('MYSQL_DSN ' ), getenv ('MYSQL_USER ' ), getenv ('MYSQL_PASSWORD ' ));
311
318
$ pdo ->setAttribute (\PDO ::ATTR_ERRMODE , \PDO ::ERRMODE_EXCEPTION );
312
319
0 commit comments