$MACHE: add support for eaccelerator/memcached, change the $MCACHE calling convention...
[moodle.git] / lib / eaccelerator.class.php
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  **/
17 class eaccelerator {
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         }
29         $this->mode   = $mode;
30         $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
31     }
33     function status() {
34         if (isset($this->mode)) {
35             return true;
36         }
37         return false;
38     }
40     function set($key, $value, $ttl=0) {
41         $set    = $this->mode . '_put';
42         $unlock = $this->mode . '_unlock';
44         // we may have acquired a lock via getforfill
45         // release if it exists
46         @$unlock($this->prefix . $key . '_forfill');
48         return $set($this->prefix . $key, serialize($value), $ttl);
49     }
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     }
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     }
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     }
130 ?>