fix git cvs drift
[moodle.git] / lib / eaccelerator.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/ 
4 // 
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 // 
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * This class abstracts eaccelerator/turckmmcache
20  * API to provide
21  * 
22  * - get()
23  * - set()
24  * - delete()
25  * - getforfill()
26  * - releaseforfill()
27  *
28  * Note: do NOT store booleans here. For compatibility with
29  * memcached, a false value is indistinguisable from a 
30  * "not found in cache" response.
31  *
32  * @copyright Martin Langhoff <martin@catalyst.net.nz>
33  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34  * @package moodlecore
35  */
37 /**
38  *
39  * @copyright Martin Langhoff <martin@catalyst.net.nz>
40  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  * @package moodlecore
42  */
43 class eaccelerator {
45     /**
46      * @todo Document this function
47      *
48      * @global object
49      */
50     function eaccelerator() {
51         global $CFG;
52         if ( function_exists('eaccelerator_get')) {
53             $this->mode = 'eaccelerator';
54         } elseif (function_exists('mmcache_get')) {
55             $this->mode = 'mmcache';
56         } else {
57             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.");
58         }
60         $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
61     }
63     /**
64      * The status of the eaccelerator, if it has been established
65      * this will return true
66      *
67      * @return bool
68      */
69     function status() {
70         if (isset($this->mode)) {
71             return true;
72         }
73         return false;
74     }
76     /**
77      * @todo Document this function
78      *
79      * @param string $key
80      * @param string $value
81      * @param int $ttl
82      * @return mixed
83      */
84     function set($key, $value, $ttl=0) {
85         $set    = $this->mode . '_put';
86         $unlock = $this->mode . '_unlock';
88         // we may have acquired a lock via getforfill
89         // release if it exists
90         @$unlock($this->prefix . $key . '_forfill');
92         return $set($this->prefix . $key, serialize($value), $ttl);
93     }
95     /**
96      * @todo Document this function
97      *
98      * @param string $key
99      * @return string|bool String if success else false
100      */
101     function get($key) {
102         $fn = $this->mode . '_get';
103         $rec = $fn($this->prefix . $key);
104         if (is_null($rec)) {
105             return false;
106         }
107         return unserialize($rec);
108     } 
109     
110     /**
111      * @todo Document this function
112      *
113      * @param string $key
114      * @return mixed
115      */    
116     function delete($key) {
117         $fn = $this->mode . '_rm';
118         return $fn($this->prefix . $key);
119     }
121     /**
122      * In the simple case, this function will 
123      * get the cached value if available. If the entry
124      * is not cached, it will try to get an exclusive
125      * lock that announces that this process will
126      * populate the cache.
127      *
128      * If we fail to get the lock -- this means another
129      * process is doing it. 
130      * so we wait (block) for a few microseconds while we wait for
131      * the cache to be filled or the lock to timeout.
132      * 
133      * If you get a false from this call, you _must_
134      * populate the cache ASAP or indicate that
135      * you won't by calling releaseforfill().
136      *
137      * This technique forces serialisation and so helps deal 
138      * with thundering herd scenarios where a lot of clients 
139      * ask the for the same idempotent (and costly) operation. 
140      * The implementation is based on suggestions in this message
141      * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
142      *
143      * @param $key string
144      * @return mixed on cache hit, false otherwise
145      */
146     function getforfill ($key) {
147         $get    = $this->mode . '_get';
148         $lock   = $this->mode . '_lock';
149         
150         $rec = $get($this->prefix . $key);
151         if (!is_null($rec)) {
152             return unserialize($rec);
153         }
154         if ($lock($this->prefix . $key . '_forfill')) {
155             // we obtained the _forfill lock
156             // our caller will compute and set the value
157             return false;
158         }
159         // someone else has the lock
160         // "block" till we can get the value
161         // actually, loop .05s waiting for it
162         for ($n=0;$n<5;$n++) {
163             usleep(10000);
164             $rec = $get($this->prefix . $key);
165             if (!is_null($rec)) {
166                 return unserialize($rec);
167             }
168         }
169         return false;
170     }
172     /**
173      * Release the exclusive lock obtained by 
174      * getforfill(). See getforfill()
175      * for more details.
176      *
177      * @param $key string
178      * @return bool
179      */
180     function releaseforfill ($key) {
181         $unlock = $this->mode . '_unlock';
182         return $unlock($this->prefix . $key . '_forfill');
183     }
186 ?>