PoolInternal::allocate()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
namespace ResourcePool;
3
4
use React\Promise\FulfilledPromise;
5
use React\Promise\Deferred;
6
7
/**
8
 * This class exists to keep the public interface of Pool clean. Once PHP 5.3 support is dropped,
9
 * this functionality will probably be moved to Pool in the form of private methods.
10
 *
11
 * @author Josh Di Fabio <[email protected]>
12
 *
13
 * @internal
14
 */
15
class PoolInternal
16
{
17
    private $size;
18
    private $usage = 0;
19
    private $queue;
20
    private $whenNextIdle;
21
22
    public function __construct($size = null)
23
    {
24
        $this->size = $size;
25
        $this->queue = new \SplQueue;
26
        $this->whenNextIdle = new Deferred;
27
        $this->whenNextIdle->resolve();
28
    }
29
30
    public function allocate($count)
31
    {
32
        return $this->createAllocationPromise('ResourcePool\PartialAllocationPromise', $count);
33
    }
34
35
    public function allocateAll()
36
    {
37
        return $this->createAllocationPromise('ResourcePool\AllocationPromise');
38
    }
39
40
    private function createAllocationPromise($promiseClass, $count = null)
41
    {
42
        $this->beforeAllocate();
43
        
44
        if ($this->canAllocate($count)) {
45
            $allocation = $this->createAllocation($count);
46
            $promise = new FulfilledPromise($allocation);
47
            $resolver = null;
48
        } else {
49
            $deferred = new Deferred;
50
            $promise = $deferred->promise();
51
            $isResolved = false;
52
            $resolver = $this->createResolver($isResolved, $count, $deferred);
53
            $promise->then(null, array($this, 'afterPromiseCancelled'));
54
            $this->queue->enqueue(array($count, $deferred, &$isResolved));
55
        }
56
57
        return new $promiseClass($promise, $resolver);
58
    }
59
    
60
    public function setSize($size)
61
    {
62
        $this->size = $size;
63
        $this->processQueue();
64
    }
65
66
    public function getAvailability()
67
    {
68
        return max(0, $this->size - $this->usage);
69
    }
70
71
    public function getUsage()
72
    {
73
        return $this->usage;
74
    }
75
76
    private function processQueue()
77
    {
78
        foreach ($this->queue as $allocationInfo) {
79
            if (true === $allocationInfo[2]) {
80
                $this->queue->dequeue();
81
                continue;
82
            }
83
84
            if (!$this->canAllocate($allocationInfo[0])) {
85
                break;
86
            }
87
88
            $this->queue->dequeue();
89
            $allocation = $this->createAllocation($allocationInfo[0]);
90
            $allocationInfo[1]->resolve($allocation);
91
        }
92
    }
93
    
94
    public function afterPromiseCancelled()
95
    {
96
        $this->decrementUsage(0);
97
    }
98
99
    public function decrementUsage($amount)
100
    {
101
        $this->usage -= $amount;
102
        $this->processQueue();
103
        if ($this->isIdle()) {
104
            $this->whenNextIdle->resolve();
105
        }
106
    }
107
108
    public function canAllocate($count = null)
109
    {
110
        if (null === $count) {
111
            return 0 === $this->usage && $this->size > 0;
112
        }
113
114
        return $count <= $this->getAvailability();
115
    }
116
117
    public function createAllocation($size)
118
    {
119
        if (null === $size) {
120
            $size = $this->size;
121
        }
122
123
        $this->usage += $size;
124
125
        return new Allocation(array($this, 'decrementUsage'), $size);
126
    }
127
128
    public function whenNextIdle($fulfilledHandler = null)
129
    {
130
        $promise = $this->whenNextIdle->promise();
131
132
        if (null !== $fulfilledHandler) {
133
            return $promise->then($fulfilledHandler);
134
        }
135
136
        return $promise;
137
    }
138
139
    private function beforeAllocate()
140
    {
141
        if ($this->isIdle()) {
142
            $this->whenNextIdle = new Deferred;
143
        }
144
    }
145
146
    private function isIdle()
147
    {
148
        return (0 == $this->usage && 0 == $this->queue->count());
149
    }
150
    
151
    private function createResolver(&$isResolved, $count, $deferred)
152
    {
153
        $that = $this;
154
        
155
        return function ($burst) use (&$isResolved, $that, $count, $deferred) {
156
            if ($isResolved) {
157
                throw new \LogicException;
158
            }
159
160
            $isResolved = true;
161
            
162
            if ($burst || $that->canAllocate($count)) {
163
                $allocation = $that->createAllocation($count);
164
                $deferred->resolve($allocation);
165
            } else {
166
                $deferred->reject(new \RuntimeException('The resource pool cannot allocate the specified number of resources'));
167
            }
168
        };
169
    }
170
}
171