File: BlockingStoreTestTrait.php

package info (click to toggle)
symfony 6.4.25%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 138,776 kB
  • sloc: php: 1,443,643; xml: 6,601; sh: 605; javascript: 597; makefile: 188; pascal: 71
file content (98 lines) | stat: -rw-r--r-- 3,121 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Lock\Tests\Store;

use Symfony\Component\Lock\Exception\LockConflictedException;
use Symfony\Component\Lock\Key;
use Symfony\Component\Lock\PersistingStoreInterface;

/**
 * @author Jérémy Derussé <jeremy@derusse.com>
 */
trait BlockingStoreTestTrait
{
    /**
     * @see AbstractStoreTestCase::getStore()
     */
    abstract protected function getStore(): PersistingStoreInterface;

    /**
     * Tests blocking locks thanks to pcntl.
     *
     * This test is time sensible: the $clockDelay could be adjust.
     *
     * It also fails when run with the global ./phpunit test suite.
     *
     * @group transient
     *
     * @requires function pcntl_sigwaitinfo
     */
    #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('pcntl')]
    #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('posix')]
    public function testBlockingLocks()
    {
        // Amount of microseconds we should wait without slowing things down too much
        $clockDelay = 50000;

        $key = new Key(uniqid(__METHOD__, true));
        $parentPID = posix_getpid();

        // Block SIGHUP signal
        pcntl_sigprocmask(\SIG_BLOCK, [\SIGHUP]);

        if ($childPID = pcntl_fork()) {
            // Wait the start of the child
            pcntl_sigwaitinfo([\SIGHUP], $info);

            $store = $this->getStore();
            try {
                // This call should failed given the lock should already by acquired by the child
                $store->save($key);
                $this->fail('The store saves a locked key.');
            } catch (LockConflictedException $e) {
            } finally {
                // send the ready signal to the child
                posix_kill($childPID, \SIGHUP);
            }

            // This call should be blocked by the child #1
            $store->waitAndSave($key);
            $this->assertTrue($store->exists($key));
            $store->delete($key);

            // Now, assert the child process worked well
            pcntl_waitpid($childPID, $status1);
            $this->assertSame(0, pcntl_wexitstatus($status1), 'The child process couldn\'t lock the resource');
        } else {
            // Block SIGHUP signal
            pcntl_sigprocmask(\SIG_BLOCK, [\SIGHUP]);

            try {
                $store = $this->getStore();
                $store->save($key);
                // send the ready signal to the parent
                posix_kill($parentPID, \SIGHUP);

                // Wait for the parent to be ready
                pcntl_sigwaitinfo([\SIGHUP], $info);

                // Wait ClockDelay to let parent assert to finish
                usleep($clockDelay);
                $store->delete($key);
                exit(0);
            } catch (\Throwable $e) {
                posix_kill($parentPID, \SIGHUP);
                exit(1);
            }
        }
    }
}