2142d492 |
1 | <?php |
2 | /** |
3 | ** This class abstracts eaccelerator/turckmmcache |
4 | ** API to provide |
5 | ** |
6 | ** - get() |
7 | ** - set() |
8 | ** - delete() |
9 | ** - getforfill() |
10 | ** - releaseforfill() |
11 | ** |
12 | ** Author: Martin Langhoff <martin@catalyst.net.nz> |
13 | ** |
14 | **/ |
15 | |
16 | |
17 | class eaccelerator { |
18 | |
19 | function eaccelerator() { |
20 | global $CFG; |
21 | if ( function_exists('eaccelerator_get')) { |
22 | $mode = 'eaccelerator'; |
23 | } elseif (function_exists('mmcache_get')) { |
24 | $mode = 'mmcache'; |
25 | } else { |
26 | debugging("\$CFG->eaccelerator is set to true but the required functions are not available. You need to have either eaccelerator or turckmmcache extensions installed, compiled with the shmem keys option enabled."); |
27 | } |
28 | |
29 | $this->mode = $mode; |
30 | $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|'; |
31 | } |
32 | |
33 | function status() { |
34 | if (isset($this->mode)) { |
35 | return true; |
36 | } |
37 | return false; |
38 | } |
39 | |
40 | function set($key, $value, $ttl=0) { |
41 | $set = $this->mode . '_put'; |
42 | $unlock = $this->mode . '_unlock'; |
43 | |
44 | // we may have acquired a lock via getforfill |
45 | // release if it exists |
46 | @$unlock($this->prefix . $key . '_forfill'); |
47 | |
48 | return $set($this->prefix . $key, serialize($value), $ttl); |
49 | } |
50 | |
51 | function get($key) { |
52 | $fn = $this->mode . '_get'; |
53 | $rec = $fn($this->prefix . $key); |
54 | if (is_null($rec)) { |
55 | return null; |
56 | } |
57 | return unserialize($rec); |
58 | } |
59 | |
60 | function delete($key) { |
61 | $fn = $this->mode . '_rm'; |
62 | return $fn($this->prefix . $key); |
63 | } |
64 | |
65 | /** |
66 | * In the simple case, this function will |
67 | * get the cached value if available. If the entry |
68 | * is not cached, it will try to get an exclusive |
69 | * lock that announces that this process will |
70 | * populate the cache. |
71 | * |
72 | * If we fail to get the lock -- this means another |
73 | * process is doing it. |
74 | * so we wait (block) for a few microseconds while we wait for |
75 | * the cache to be filled or the lock to timeout. |
76 | * |
77 | * If you get a false from this call, you _must_ |
78 | * populate the cache ASAP or indicate that |
79 | * you won't by calling releaseforfill(). |
80 | * |
81 | * This technique forces serialisation and so helps deal |
82 | * with thundering herd scenarios where a lot of clients |
83 | * ask the for the same idempotent (and costly) operation. |
84 | * The implementation is based on suggestions in this message |
85 | * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2 |
86 | * |
87 | * @param $key string |
88 | * @return mixed on cache hit, NULL otherwise |
89 | */ |
90 | function getforfill ($key) { |
91 | $get = $this->mode . '_get'; |
92 | $lock = $this->mode . '_lock'; |
93 | |
94 | $rec = $get($this->prefix . $key); |
95 | if (!is_null($rec)) { |
96 | return unserialize($rec); |
97 | } |
98 | if ($lock($this->prefix . $key . '_forfill')) { |
99 | // we obtained the _forfill lock |
100 | // our caller will compute and set the value |
101 | return null; |
102 | } |
103 | // someone else has the lock |
104 | // "block" till we can get the value |
105 | // actually, loop .05s waiting for it |
106 | for ($n=0;$n<5;$n++) { |
107 | usleep(10000); |
108 | $rec = $get($this->prefix . $key); |
109 | if (!is_null($rec)) { |
110 | return unserialize($rec); |
111 | } |
112 | } |
113 | return null; |
114 | } |
115 | |
116 | /** |
117 | * Release the exclusive lock obtained by |
118 | * getforfill(). See getforfill() |
119 | * for more details. |
120 | * |
121 | * @param $key string |
122 | * @return bool |
123 | */ |
124 | function releaseforfill ($key) { |
125 | $unlock = $this->mode . '_unlock'; |
126 | return $unlock($this->prefix . $key . '_forfill'); |
127 | } |
128 | } |
129 | |
130 | ?> |