Index: branches/wmf/1.17wmf1/extensions/PoolCounter/poolcounter.py |
— | — | @@ -0,0 +1,228 @@ |
| 2 | +#!/usr/bin/env python |
| 3 | +# |
| 4 | +# Better run this with twistd -r epoll -y poolcounter.py -u nobody -g nogroup |
| 5 | +# |
| 6 | + |
| 7 | +from twisted.internet import reactor, protocol |
| 8 | +from twisted.protocols import basic, policies |
| 9 | +from twisted.application import internet, service |
| 10 | + |
| 11 | +import time |
| 12 | + |
| 13 | +port=7531 |
| 14 | + |
| 15 | +class Error: |
| 16 | + badcomm = (1, "Bad command or not enough arguments") |
| 17 | + nokey = (2, "%s No such key acquired by current connection") |
| 18 | + haveit = (3, "%s Already acquired") |
| 19 | + |
| 20 | + def __init__(self,error,params=[]): |
| 21 | + self.errno=error[0] |
| 22 | + self.message=error[1] |
| 23 | + self.params=params |
| 24 | + pass |
| 25 | + |
| 26 | + def msg(self): |
| 27 | + return "ERROR %03d %s\n" % (self.errno, self.message % self.params) |
| 28 | + |
| 29 | + |
| 30 | +class CounterDatabase(dict): |
| 31 | + """Counter database organizes counter objects by their name, |
| 32 | + and provides basis for on-demand creation and destruction of counters |
| 33 | + |
| 34 | + It maps: |
| 35 | + 1:1 with CounterFactory |
| 36 | + 1:n with Counter (linked both sides) |
| 37 | + """ |
| 38 | + def acquireCounter(self, name, client,options={}): |
| 39 | + """Acquires (and if needed) creates a counter |
| 40 | + for a client, as well as increments counts""" |
| 41 | + |
| 42 | + if name not in self: |
| 43 | + counter = Counter(name, self) |
| 44 | + else: |
| 45 | + counter = self[name] |
| 46 | + |
| 47 | + if client in counter: |
| 48 | + raise Error(Error.haveit, name) |
| 49 | + |
| 50 | + counter.acquire(client) |
| 51 | + return counter |
| 52 | + |
| 53 | + def releaseCounter(self, name, client, options={}): |
| 54 | + """Releases, and if necessary, destroys counter object""" |
| 55 | + try: |
| 56 | + counter = self[name] |
| 57 | + counter[client] |
| 58 | + except KeyError: |
| 59 | + raise Error(Error.nokey, name) |
| 60 | + |
| 61 | + # Counter manages it's own membership inside the database |
| 62 | + counter.release(client) |
| 63 | + |
| 64 | + |
| 65 | +class Counter(dict): |
| 66 | + """Counter object tracks and counts |
| 67 | + clients that have acquired it""" |
| 68 | + # count is size of dictionary |
| 69 | + count = property(fget=len) |
| 70 | + age = property(fget = lambda self: int(time.time() - self.init_time)) |
| 71 | + |
| 72 | + def __hash__(self): |
| 73 | + """Make this object usable as key in other dictionaries, |
| 74 | + after losing such property by inheriting (dict)""" |
| 75 | + return id(self); |
| 76 | + |
| 77 | + def __init__(self, name, db): |
| 78 | + """Register object in supplied database""" |
| 79 | + self.database = db |
| 80 | + self.name = name |
| 81 | + self.database[name] = self |
| 82 | + self.init_time = time.time() |
| 83 | + |
| 84 | + def acquire(self, client): |
| 85 | + """Register both client inside counter, and counter at client""" |
| 86 | + self[client] = True |
| 87 | + client.counts[self] = True |
| 88 | + |
| 89 | + def release(self, client): |
| 90 | + del self[client] |
| 91 | + del client.counts[self] |
| 92 | + |
| 93 | + if len(self) == 0: |
| 94 | + del self.database[self.name] |
| 95 | + |
| 96 | + |
| 97 | +class CounterClient(basic.LineReceiver,policies.TimeoutMixin): |
| 98 | + """Counter protocol, basic functions and connection tracking""" |
| 99 | + def __init__(self): |
| 100 | + """Initialize counter objects held by a client""" |
| 101 | + self.counts = {} |
| 102 | + |
| 103 | + def error(self, error, parts=[]): |
| 104 | + """Write an error, based either on exception message or custom error tuple""" |
| 105 | + if isinstance(error,Error): |
| 106 | + self.transport.write(error.msg()) |
| 107 | + else: |
| 108 | + self.transport.write("ERROR %03d %s\n" % (error[0], error[1] % parts) ) |
| 109 | + |
| 110 | + def connectionMade(self): |
| 111 | + """When connection is established, add it to factory |
| 112 | + public list of connections, and set timeouts""" |
| 113 | + self.factory.conns[self] = True |
| 114 | + self.setTimeout(300) |
| 115 | + |
| 116 | + def lineReceived(self,line): |
| 117 | + """Process the request line, and invoke necessary actions""" |
| 118 | + |
| 119 | + request = line.split() |
| 120 | + |
| 121 | + self.resetTimeout() |
| 122 | + self.factory.stats_requests += 1 |
| 123 | + |
| 124 | + # Development, debugging and introspection functions |
| 125 | + if (len(request)<=1): |
| 126 | + if len(request) == 0: |
| 127 | + pass |
| 128 | + elif request[0] == "quit": |
| 129 | + self.transport.loseConnection() |
| 130 | + # Show counts acquired by connections |
| 131 | + elif request[0] == "conns": |
| 132 | + self.transport.write( str([(k, [c.name for c in k.counts]) |
| 133 | + for k in self.factory.conns])+"\n") |
| 134 | + # Show counter values |
| 135 | + elif request[0] == "counts": |
| 136 | + self.transport.write(str([(k,v.count) for k,v in |
| 137 | + self.factory.database.items()])+ "\n") |
| 138 | + # Just die :) |
| 139 | + elif request[0] == "die": |
| 140 | + reactor.stop() |
| 141 | + # Basic statistics |
| 142 | + elif request[0] == "stats": |
| 143 | + self.transport.write("Counters: %d\nConnections: %d\nRequests: %d\n" % |
| 144 | + (len(self.factory.database), len(self.factory.conns), self.factory.stats_requests)) |
| 145 | + else: |
| 146 | + self.error(Error.badcomm) |
| 147 | + return |
| 148 | + |
| 149 | + # From here on verbs need nouns and more |
| 150 | + # This is where real work starts |
| 151 | + # First we save options provided |
| 152 | + options = dict([len(y.split(":", 2)) > 1 and y.split(":", 2) or (y, True) |
| 153 | + for y in request[2:]]) |
| 154 | + |
| 155 | + verb = request[0] |
| 156 | + key = request[1] |
| 157 | + |
| 158 | + # Acquire can specify: |
| 159 | + # XXX - notification thresholds |
| 160 | + # XXX - ??? |
| 161 | + # XXX - Profit!!! |
| 162 | + |
| 163 | + if verb == "acquire": |
| 164 | + # Parse the options, format is: a:x b c:y ... |
| 165 | + try: |
| 166 | + counter = self.factory.database.acquireCounter(key, self, options) |
| 167 | + except Error, e: |
| 168 | + self.error(e) |
| 169 | + return |
| 170 | + self.transport.write("ACK %s count:%d age:%d\n" % (key, counter.count, counter.age) ) |
| 171 | + # Release can specify options: |
| 172 | + # XXX - abandoned (don't notify) |
| 173 | + elif verb == "release": |
| 174 | + try: |
| 175 | + self.factory.database.releaseCounter(key, self, options) |
| 176 | + except Error, e: |
| 177 | + self.error(e) |
| 178 | + return |
| 179 | + self.transport.write("RELEASED %s\n" % key) |
| 180 | + # Noop counter fetch |
| 181 | + elif verb == "count": |
| 182 | + if key in self.factory.database: |
| 183 | + counter=self.factory.database[key] |
| 184 | + count=counter.count |
| 185 | + age=counter.age |
| 186 | + else: |
| 187 | + count=0 |
| 188 | + age=0 |
| 189 | + self.transport.write("COUNT %s count:%d age:%d\n" % (key, count) ) |
| 190 | + else: |
| 191 | + self.error(Error.badcomm) |
| 192 | + return |
| 193 | + |
| 194 | + def connectionLost(self, reason): |
| 195 | + """Abandon counters and deregister connection on lost connection in case: |
| 196 | + * Disconnected |
| 197 | + * "quit" command received |
| 198 | + * Timeout hit |
| 199 | + """ |
| 200 | + for counter in self.counts.copy(): |
| 201 | + counter.release(self) |
| 202 | + del self.factory.conns[self] |
| 203 | + |
| 204 | + |
| 205 | +class CounterFactory(protocol.ServerFactory): |
| 206 | + """Counter instance object, owns database and bunch of clients""" |
| 207 | + protocol = CounterClient |
| 208 | + |
| 209 | + def __init__(self): |
| 210 | + self.database = CounterDatabase() |
| 211 | + self.stats_requests = 0 |
| 212 | + self.conns = {} |
| 213 | + |
| 214 | +# Linux reactor |
| 215 | +try: |
| 216 | + from twisted.internet import epollreactor |
| 217 | + epollreactor.install() |
| 218 | +except: pass |
| 219 | + |
| 220 | +factory = CounterFactory() |
| 221 | +application = service.Application("poolcounter") |
| 222 | +counterservice = internet.TCPServer(port, factory) |
| 223 | +counterservice.setServiceParent(application) |
| 224 | + |
| 225 | +# If invoked directly and ot via twistd... |
| 226 | +if __name__=="__main__": |
| 227 | + reactor.listenTCP(port, factory) |
| 228 | + reactor.run() |
| 229 | + |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/poolcounter.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 230 | + native |
Added: svn:executable |
2 | 231 | + * |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/locks.h |
— | — | @@ -0,0 +1,53 @@ |
| 2 | +#ifndef LOCKS_H |
| 3 | +#define LOCKS_H |
| 4 | + |
| 5 | +#include <stdint.h> |
| 6 | + |
| 7 | +/* This application uses several double linked lists. |
| 8 | + * They are circular lists, new items are added on the end (ie. on prev) |
| 9 | + * and popped from next. |
| 10 | + */ |
| 11 | +struct double_linked_list { |
| 12 | + struct double_linked_list* prev; |
| 13 | + struct double_linked_list* next; |
| 14 | +}; |
| 15 | + |
| 16 | +struct hashtable_entry { |
| 17 | + struct double_linked_list hashtable_siblings; |
| 18 | + struct hashtable* parent_hashtable; |
| 19 | + uint32_t key_hash; |
| 20 | + char* key; |
| 21 | +}; |
| 22 | + |
| 23 | +struct PoolCounter { |
| 24 | + struct hashtable_entry htentry; |
| 25 | + |
| 26 | + uint32_t count; |
| 27 | + int processing; |
| 28 | + |
| 29 | + struct double_linked_list working; |
| 30 | + struct double_linked_list for_them; |
| 31 | + struct double_linked_list for_anyone; |
| 32 | +}; |
| 33 | + |
| 34 | +struct locks { |
| 35 | + struct double_linked_list siblings; |
| 36 | + struct PoolCounter* parent; |
| 37 | + enum lock_state { UNLOCKED, WAITING, WAIT_ANY, PROCESSING } state; |
| 38 | +}; |
| 39 | + |
| 40 | +struct client_data; |
| 41 | +void init_lock(struct locks* l); |
| 42 | +void finish_lock(struct locks* l); |
| 43 | +char* process_line(struct client_data* cli_data, char* line, int line_len); |
| 44 | +void process_timeout(struct locks* l); |
| 45 | +void remove_client_lock(struct locks* l, int wakeup_anyones); |
| 46 | +void send_client(struct locks* l, const char* msg); |
| 47 | + |
| 48 | + |
| 49 | +void hashtable_init(); |
| 50 | +struct hashtable* hashtable_create(int hashpower); |
| 51 | +void* hashtable_find(struct hashtable* ht, uint32_t hash_value, const char* key); |
| 52 | +void hashtable_insert(struct hashtable* ht, struct hashtable_entry* htentry); |
| 53 | +void hashtable_remove(struct hashtable* ht, struct hashtable_entry* htentry); |
| 54 | +#endif |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/locks.h |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 55 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/hash.c |
— | — | @@ -0,0 +1,432 @@ |
| 2 | +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 3 | +/* |
| 4 | + * Hash table |
| 5 | + * |
| 6 | + * The hash function used here is by Bob Jenkins, 1996: |
| 7 | + * <http://burtleburtle.net/bob/hash/doobs.html> |
| 8 | + * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. |
| 9 | + * You may use this code any way you wish, private, educational, |
| 10 | + * or commercial. It's free." |
| 11 | + * |
| 12 | + */ |
| 13 | +#include <stddef.h> |
| 14 | +#include <inttypes.h> |
| 15 | + |
| 16 | +/* |
| 17 | + * Since the hash function does bit manipulation, it needs to know |
| 18 | + * whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG |
| 19 | + * are set in the configure script. |
| 20 | + */ |
| 21 | +#if ENDIAN_BIG == 1 |
| 22 | +# define HASH_LITTLE_ENDIAN 0 |
| 23 | +# define HASH_BIG_ENDIAN 1 |
| 24 | +#else |
| 25 | +# if ENDIAN_LITTLE == 1 |
| 26 | +# define HASH_LITTLE_ENDIAN 1 |
| 27 | +# define HASH_BIG_ENDIAN 0 |
| 28 | +# else |
| 29 | +# define HASH_LITTLE_ENDIAN 0 |
| 30 | +# define HASH_BIG_ENDIAN 0 |
| 31 | +# endif |
| 32 | +#endif |
| 33 | + |
| 34 | +#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k)))) |
| 35 | + |
| 36 | +/* |
| 37 | +------------------------------------------------------------------------------- |
| 38 | +mix -- mix 3 32-bit values reversibly. |
| 39 | + |
| 40 | +This is reversible, so any information in (a,b,c) before mix() is |
| 41 | +still in (a,b,c) after mix(). |
| 42 | + |
| 43 | +If four pairs of (a,b,c) inputs are run through mix(), or through |
| 44 | +mix() in reverse, there are at least 32 bits of the output that |
| 45 | +are sometimes the same for one pair and different for another pair. |
| 46 | +This was tested for: |
| 47 | +* pairs that differed by one bit, by two bits, in any combination |
| 48 | + of top bits of (a,b,c), or in any combination of bottom bits of |
| 49 | + (a,b,c). |
| 50 | +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed |
| 51 | + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as |
| 52 | + is commonly produced by subtraction) look like a single 1-bit |
| 53 | + difference. |
| 54 | +* the base values were pseudorandom, all zero but one bit set, or |
| 55 | + all zero plus a counter that starts at zero. |
| 56 | + |
| 57 | +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that |
| 58 | +satisfy this are |
| 59 | + 4 6 8 16 19 4 |
| 60 | + 9 15 3 18 27 15 |
| 61 | + 14 9 3 7 17 3 |
| 62 | +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing |
| 63 | +for "differ" defined as + with a one-bit base and a two-bit delta. I |
| 64 | +used http://burtleburtle.net/bob/hash/avalanche.html to choose |
| 65 | +the operations, constants, and arrangements of the variables. |
| 66 | + |
| 67 | +This does not achieve avalanche. There are input bits of (a,b,c) |
| 68 | +that fail to affect some output bits of (a,b,c), especially of a. The |
| 69 | +most thoroughly mixed value is c, but it doesn't really even achieve |
| 70 | +avalanche in c. |
| 71 | + |
| 72 | +This allows some parallelism. Read-after-writes are good at doubling |
| 73 | +the number of bits affected, so the goal of mixing pulls in the opposite |
| 74 | +direction as the goal of parallelism. I did what I could. Rotates |
| 75 | +seem to cost as much as shifts on every machine I could lay my hands |
| 76 | +on, and rotates are much kinder to the top and bottom bits, so I used |
| 77 | +rotates. |
| 78 | +------------------------------------------------------------------------------- |
| 79 | +*/ |
| 80 | +#define mix(a,b,c) \ |
| 81 | +{ \ |
| 82 | + a -= c; a ^= rot(c, 4); c += b; \ |
| 83 | + b -= a; b ^= rot(a, 6); a += c; \ |
| 84 | + c -= b; c ^= rot(b, 8); b += a; \ |
| 85 | + a -= c; a ^= rot(c,16); c += b; \ |
| 86 | + b -= a; b ^= rot(a,19); a += c; \ |
| 87 | + c -= b; c ^= rot(b, 4); b += a; \ |
| 88 | +} |
| 89 | + |
| 90 | +/* |
| 91 | +------------------------------------------------------------------------------- |
| 92 | +final -- final mixing of 3 32-bit values (a,b,c) into c |
| 93 | + |
| 94 | +Pairs of (a,b,c) values differing in only a few bits will usually |
| 95 | +produce values of c that look totally different. This was tested for |
| 96 | +* pairs that differed by one bit, by two bits, in any combination |
| 97 | + of top bits of (a,b,c), or in any combination of bottom bits of |
| 98 | + (a,b,c). |
| 99 | +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed |
| 100 | + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as |
| 101 | + is commonly produced by subtraction) look like a single 1-bit |
| 102 | + difference. |
| 103 | +* the base values were pseudorandom, all zero but one bit set, or |
| 104 | + all zero plus a counter that starts at zero. |
| 105 | + |
| 106 | +These constants passed: |
| 107 | + 14 11 25 16 4 14 24 |
| 108 | + 12 14 25 16 4 14 24 |
| 109 | +and these came close: |
| 110 | + 4 8 15 26 3 22 24 |
| 111 | + 10 8 15 26 3 22 24 |
| 112 | + 11 8 15 26 3 22 24 |
| 113 | +------------------------------------------------------------------------------- |
| 114 | +*/ |
| 115 | +#define final(a,b,c) \ |
| 116 | +{ \ |
| 117 | + c ^= b; c -= rot(b,14); \ |
| 118 | + a ^= c; a -= rot(c,11); \ |
| 119 | + b ^= a; b -= rot(a,25); \ |
| 120 | + c ^= b; c -= rot(b,16); \ |
| 121 | + a ^= c; a -= rot(c,4); \ |
| 122 | + b ^= a; b -= rot(a,14); \ |
| 123 | + c ^= b; c -= rot(b,24); \ |
| 124 | +} |
| 125 | + |
| 126 | +#if HASH_LITTLE_ENDIAN == 1 |
| 127 | +uint32_t hash( |
| 128 | + const void *key, /* the key to hash */ |
| 129 | + size_t length, /* length of the key */ |
| 130 | + const uint32_t initval) /* initval */ |
| 131 | +{ |
| 132 | + uint32_t a,b,c; /* internal state */ |
| 133 | + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ |
| 134 | + |
| 135 | + /* Set up the internal state */ |
| 136 | + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; |
| 137 | + |
| 138 | + u.ptr = key; |
| 139 | + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { |
| 140 | + const uint32_t *k = key; /* read 32-bit chunks */ |
| 141 | +#ifdef VALGRIND |
| 142 | + const uint8_t *k8; |
| 143 | +#endif /* ifdef VALGRIND */ |
| 144 | + |
| 145 | + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ |
| 146 | + while (length > 12) |
| 147 | + { |
| 148 | + a += k[0]; |
| 149 | + b += k[1]; |
| 150 | + c += k[2]; |
| 151 | + mix(a,b,c); |
| 152 | + length -= 12; |
| 153 | + k += 3; |
| 154 | + } |
| 155 | + |
| 156 | + /*----------------------------- handle the last (probably partial) block */ |
| 157 | + /* |
| 158 | + * "k[2]&0xffffff" actually reads beyond the end of the string, but |
| 159 | + * then masks off the part it's not allowed to read. Because the |
| 160 | + * string is aligned, the masked-off tail is in the same word as the |
| 161 | + * rest of the string. Every machine with memory protection I've seen |
| 162 | + * does it on word boundaries, so is OK with this. But VALGRIND will |
| 163 | + * still catch it and complain. The masking trick does make the hash |
| 164 | + * noticably faster for short strings (like English words). |
| 165 | + */ |
| 166 | +#ifndef VALGRIND |
| 167 | + |
| 168 | + switch(length) |
| 169 | + { |
| 170 | + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; |
| 171 | + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; |
| 172 | + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; |
| 173 | + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; |
| 174 | + case 8 : b+=k[1]; a+=k[0]; break; |
| 175 | + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; |
| 176 | + case 6 : b+=k[1]&0xffff; a+=k[0]; break; |
| 177 | + case 5 : b+=k[1]&0xff; a+=k[0]; break; |
| 178 | + case 4 : a+=k[0]; break; |
| 179 | + case 3 : a+=k[0]&0xffffff; break; |
| 180 | + case 2 : a+=k[0]&0xffff; break; |
| 181 | + case 1 : a+=k[0]&0xff; break; |
| 182 | + case 0 : return c; /* zero length strings require no mixing */ |
| 183 | + } |
| 184 | + |
| 185 | +#else /* make valgrind happy */ |
| 186 | + |
| 187 | + k8 = (const uint8_t *)k; |
| 188 | + switch(length) |
| 189 | + { |
| 190 | + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; |
| 191 | + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ |
| 192 | + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ |
| 193 | + case 9 : c+=k8[8]; /* fall through */ |
| 194 | + case 8 : b+=k[1]; a+=k[0]; break; |
| 195 | + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ |
| 196 | + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ |
| 197 | + case 5 : b+=k8[4]; /* fall through */ |
| 198 | + case 4 : a+=k[0]; break; |
| 199 | + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ |
| 200 | + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ |
| 201 | + case 1 : a+=k8[0]; break; |
| 202 | + case 0 : return c; /* zero length strings require no mixing */ |
| 203 | + } |
| 204 | + |
| 205 | +#endif /* !valgrind */ |
| 206 | + |
| 207 | + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { |
| 208 | + const uint16_t *k = key; /* read 16-bit chunks */ |
| 209 | + const uint8_t *k8; |
| 210 | + |
| 211 | + /*--------------- all but last block: aligned reads and different mixing */ |
| 212 | + while (length > 12) |
| 213 | + { |
| 214 | + a += k[0] + (((uint32_t)k[1])<<16); |
| 215 | + b += k[2] + (((uint32_t)k[3])<<16); |
| 216 | + c += k[4] + (((uint32_t)k[5])<<16); |
| 217 | + mix(a,b,c); |
| 218 | + length -= 12; |
| 219 | + k += 6; |
| 220 | + } |
| 221 | + |
| 222 | + /*----------------------------- handle the last (probably partial) block */ |
| 223 | + k8 = (const uint8_t *)k; |
| 224 | + switch(length) |
| 225 | + { |
| 226 | + case 12: c+=k[4]+(((uint32_t)k[5])<<16); |
| 227 | + b+=k[2]+(((uint32_t)k[3])<<16); |
| 228 | + a+=k[0]+(((uint32_t)k[1])<<16); |
| 229 | + break; |
| 230 | + case 11: c+=((uint32_t)k8[10])<<16; /* @fallthrough */ |
| 231 | + case 10: c+=k[4]; /* @fallthrough@ */ |
| 232 | + b+=k[2]+(((uint32_t)k[3])<<16); |
| 233 | + a+=k[0]+(((uint32_t)k[1])<<16); |
| 234 | + break; |
| 235 | + case 9 : c+=k8[8]; /* @fallthrough */ |
| 236 | + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); |
| 237 | + a+=k[0]+(((uint32_t)k[1])<<16); |
| 238 | + break; |
| 239 | + case 7 : b+=((uint32_t)k8[6])<<16; /* @fallthrough */ |
| 240 | + case 6 : b+=k[2]; |
| 241 | + a+=k[0]+(((uint32_t)k[1])<<16); |
| 242 | + break; |
| 243 | + case 5 : b+=k8[4]; /* @fallthrough */ |
| 244 | + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); |
| 245 | + break; |
| 246 | + case 3 : a+=((uint32_t)k8[2])<<16; /* @fallthrough */ |
| 247 | + case 2 : a+=k[0]; |
| 248 | + break; |
| 249 | + case 1 : a+=k8[0]; |
| 250 | + break; |
| 251 | + case 0 : return c; /* zero length strings require no mixing */ |
| 252 | + } |
| 253 | + |
| 254 | + } else { /* need to read the key one byte at a time */ |
| 255 | + const uint8_t *k = key; |
| 256 | + |
| 257 | + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ |
| 258 | + while (length > 12) |
| 259 | + { |
| 260 | + a += k[0]; |
| 261 | + a += ((uint32_t)k[1])<<8; |
| 262 | + a += ((uint32_t)k[2])<<16; |
| 263 | + a += ((uint32_t)k[3])<<24; |
| 264 | + b += k[4]; |
| 265 | + b += ((uint32_t)k[5])<<8; |
| 266 | + b += ((uint32_t)k[6])<<16; |
| 267 | + b += ((uint32_t)k[7])<<24; |
| 268 | + c += k[8]; |
| 269 | + c += ((uint32_t)k[9])<<8; |
| 270 | + c += ((uint32_t)k[10])<<16; |
| 271 | + c += ((uint32_t)k[11])<<24; |
| 272 | + mix(a,b,c); |
| 273 | + length -= 12; |
| 274 | + k += 12; |
| 275 | + } |
| 276 | + |
| 277 | + /*-------------------------------- last block: affect all 32 bits of (c) */ |
| 278 | + switch(length) /* all the case statements fall through */ |
| 279 | + { |
| 280 | + case 12: c+=((uint32_t)k[11])<<24; |
| 281 | + case 11: c+=((uint32_t)k[10])<<16; |
| 282 | + case 10: c+=((uint32_t)k[9])<<8; |
| 283 | + case 9 : c+=k[8]; |
| 284 | + case 8 : b+=((uint32_t)k[7])<<24; |
| 285 | + case 7 : b+=((uint32_t)k[6])<<16; |
| 286 | + case 6 : b+=((uint32_t)k[5])<<8; |
| 287 | + case 5 : b+=k[4]; |
| 288 | + case 4 : a+=((uint32_t)k[3])<<24; |
| 289 | + case 3 : a+=((uint32_t)k[2])<<16; |
| 290 | + case 2 : a+=((uint32_t)k[1])<<8; |
| 291 | + case 1 : a+=k[0]; |
| 292 | + break; |
| 293 | + case 0 : return c; /* zero length strings require no mixing */ |
| 294 | + } |
| 295 | + } |
| 296 | + |
| 297 | + final(a,b,c); |
| 298 | + return c; /* zero length strings require no mixing */ |
| 299 | +} |
| 300 | + |
| 301 | +#elif HASH_BIG_ENDIAN == 1 |
| 302 | +/* |
| 303 | + * hashbig(): |
| 304 | + * This is the same as hashword() on big-endian machines. It is different |
| 305 | + * from hashlittle() on all machines. hashbig() takes advantage of |
| 306 | + * big-endian byte ordering. |
| 307 | + */ |
| 308 | +uint32_t hash( const void *key, size_t length, const uint32_t initval) |
| 309 | +{ |
| 310 | + uint32_t a,b,c; |
| 311 | + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ |
| 312 | + |
| 313 | + /* Set up the internal state */ |
| 314 | + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; |
| 315 | + |
| 316 | + u.ptr = key; |
| 317 | + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { |
| 318 | + const uint32_t *k = key; /* read 32-bit chunks */ |
| 319 | +#ifdef VALGRIND |
| 320 | + const uint8_t *k8; |
| 321 | +#endif /* ifdef VALGRIND */ |
| 322 | + |
| 323 | + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ |
| 324 | + while (length > 12) |
| 325 | + { |
| 326 | + a += k[0]; |
| 327 | + b += k[1]; |
| 328 | + c += k[2]; |
| 329 | + mix(a,b,c); |
| 330 | + length -= 12; |
| 331 | + k += 3; |
| 332 | + } |
| 333 | + |
| 334 | + /*----------------------------- handle the last (probably partial) block */ |
| 335 | + /* |
| 336 | + * "k[2]<<8" actually reads beyond the end of the string, but |
| 337 | + * then shifts out the part it's not allowed to read. Because the |
| 338 | + * string is aligned, the illegal read is in the same word as the |
| 339 | + * rest of the string. Every machine with memory protection I've seen |
| 340 | + * does it on word boundaries, so is OK with this. But VALGRIND will |
| 341 | + * still catch it and complain. The masking trick does make the hash |
| 342 | + * noticably faster for short strings (like English words). |
| 343 | + */ |
| 344 | +#ifndef VALGRIND |
| 345 | + |
| 346 | + switch(length) |
| 347 | + { |
| 348 | + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; |
| 349 | + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; |
| 350 | + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; |
| 351 | + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; |
| 352 | + case 8 : b+=k[1]; a+=k[0]; break; |
| 353 | + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; |
| 354 | + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; |
| 355 | + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; |
| 356 | + case 4 : a+=k[0]; break; |
| 357 | + case 3 : a+=k[0]&0xffffff00; break; |
| 358 | + case 2 : a+=k[0]&0xffff0000; break; |
| 359 | + case 1 : a+=k[0]&0xff000000; break; |
| 360 | + case 0 : return c; /* zero length strings require no mixing */ |
| 361 | + } |
| 362 | + |
| 363 | +#else /* make valgrind happy */ |
| 364 | + |
| 365 | + k8 = (const uint8_t *)k; |
| 366 | + switch(length) /* all the case statements fall through */ |
| 367 | + { |
| 368 | + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; |
| 369 | + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ |
| 370 | + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ |
| 371 | + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ |
| 372 | + case 8 : b+=k[1]; a+=k[0]; break; |
| 373 | + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ |
| 374 | + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ |
| 375 | + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ |
| 376 | + case 4 : a+=k[0]; break; |
| 377 | + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ |
| 378 | + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ |
| 379 | + case 1 : a+=((uint32_t)k8[0])<<24; break; |
| 380 | + case 0 : return c; |
| 381 | + } |
| 382 | + |
| 383 | +#endif /* !VALGRIND */ |
| 384 | + |
| 385 | + } else { /* need to read the key one byte at a time */ |
| 386 | + const uint8_t *k = key; |
| 387 | + |
| 388 | + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ |
| 389 | + while (length > 12) |
| 390 | + { |
| 391 | + a += ((uint32_t)k[0])<<24; |
| 392 | + a += ((uint32_t)k[1])<<16; |
| 393 | + a += ((uint32_t)k[2])<<8; |
| 394 | + a += ((uint32_t)k[3]); |
| 395 | + b += ((uint32_t)k[4])<<24; |
| 396 | + b += ((uint32_t)k[5])<<16; |
| 397 | + b += ((uint32_t)k[6])<<8; |
| 398 | + b += ((uint32_t)k[7]); |
| 399 | + c += ((uint32_t)k[8])<<24; |
| 400 | + c += ((uint32_t)k[9])<<16; |
| 401 | + c += ((uint32_t)k[10])<<8; |
| 402 | + c += ((uint32_t)k[11]); |
| 403 | + mix(a,b,c); |
| 404 | + length -= 12; |
| 405 | + k += 12; |
| 406 | + } |
| 407 | + |
| 408 | + /*-------------------------------- last block: affect all 32 bits of (c) */ |
| 409 | + switch(length) /* all the case statements fall through */ |
| 410 | + { |
| 411 | + case 12: c+=k[11]; |
| 412 | + case 11: c+=((uint32_t)k[10])<<8; |
| 413 | + case 10: c+=((uint32_t)k[9])<<16; |
| 414 | + case 9 : c+=((uint32_t)k[8])<<24; |
| 415 | + case 8 : b+=k[7]; |
| 416 | + case 7 : b+=((uint32_t)k[6])<<8; |
| 417 | + case 6 : b+=((uint32_t)k[5])<<16; |
| 418 | + case 5 : b+=((uint32_t)k[4])<<24; |
| 419 | + case 4 : a+=k[3]; |
| 420 | + case 3 : a+=((uint32_t)k[2])<<8; |
| 421 | + case 2 : a+=((uint32_t)k[1])<<16; |
| 422 | + case 1 : a+=((uint32_t)k[0])<<24; |
| 423 | + break; |
| 424 | + case 0 : return c; |
| 425 | + } |
| 426 | + } |
| 427 | + |
| 428 | + final(a,b,c); |
| 429 | + return c; |
| 430 | +} |
| 431 | +#else /* HASH_XXX_ENDIAN == 1 */ |
| 432 | +#error Must define HASH_BIG_ENDIAN or HASH_LITTLE_ENDIAN |
| 433 | +#endif /* HASH_XXX_ENDIAN == 1 */ |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/hash.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 434 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/main.c |
— | — | @@ -0,0 +1,199 @@ |
| 2 | +#define _GNU_SOURCE |
| 3 | +#include <sys/socket.h> |
| 4 | +#include <arpa/inet.h> |
| 5 | +#include <sys/stat.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <unistd.h> |
| 8 | +#include <signal.h> |
| 9 | +#include <stdio.h> |
| 10 | +#include <event.h> |
| 11 | +#include <fcntl.h> |
| 12 | + |
| 13 | +#include "client_data.h" |
| 14 | +#include "prototypes.h" |
| 15 | +#include "locks.h" |
| 16 | + |
| 17 | +static int open_sockets = 1; /* Program will automatically close when this reaches 0 */ |
| 18 | + |
| 19 | +static struct event listener_ev; |
| 20 | +int main(int argc, char** argv) { |
| 21 | + struct event_base *base; |
| 22 | + struct stat st; |
| 23 | + int listener; |
| 24 | + |
| 25 | + if ( fstat( 0, &st ) || ! S_ISSOCK( st.st_mode ) ) { |
| 26 | + close( 0 ); /* Place the listener socket in fd 0 */ |
| 27 | + listener = listensocket( PORT ); |
| 28 | + } else { |
| 29 | + /* We have been given the listening socket in stdin */ |
| 30 | + listener = 0; |
| 31 | + } |
| 32 | + |
| 33 | + setup_signals(); |
| 34 | + |
| 35 | + hashtable_init(); |
| 36 | + base = event_init(); |
| 37 | + if (!base) { |
| 38 | + fprintf( stderr, "Error in libevent initialization\n" ); |
| 39 | + return 1; |
| 40 | + } |
| 41 | + |
| 42 | + event_set( &listener_ev, listener, EV_READ | EV_PERSIST, on_connect, NULL ); |
| 43 | + |
| 44 | + event_add( &listener_ev, NULL ); |
| 45 | + |
| 46 | + event_dispatch(); |
| 47 | + |
| 48 | + event_del( &listener_ev ); |
| 49 | + |
| 50 | + event_base_free( base ); |
| 51 | + |
| 52 | + return 0; |
| 53 | +} |
| 54 | + |
| 55 | +int listensocket(short int port) /* prototype */ |
| 56 | +{ |
| 57 | + int s; |
| 58 | + struct sockaddr_in addr; |
| 59 | + |
| 60 | + if ( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { |
| 61 | + perror("Couldn't create TCP socket"); |
| 62 | + exit(1); |
| 63 | + } |
| 64 | + |
| 65 | + addr.sin_family = AF_INET; |
| 66 | + addr.sin_port = htons(port); |
| 67 | + addr.sin_addr.s_addr = INADDR_ANY; |
| 68 | + |
| 69 | + if ( bind( s, (struct sockaddr *)&addr, sizeof( struct sockaddr ) ) == -1 ) { |
| 70 | + perror("Couldn't bind"); |
| 71 | + close( s ); |
| 72 | + exit( 1 ); |
| 73 | + } |
| 74 | + |
| 75 | + if (listen( s, BACKLOG ) == -1) { |
| 76 | + perror("Couldn't listen"); |
| 77 | + close( s ); |
| 78 | + exit( 1 ); |
| 79 | + } |
| 80 | + |
| 81 | + return s; |
| 82 | +} |
| 83 | + |
| 84 | +void on_connect(int listener, short type, void* arg) /* prototype */ |
| 85 | +{ |
| 86 | + struct client_data* cli; |
| 87 | + int fd; |
| 88 | +#if HAVE_ACCEPT4 |
| 89 | + fd = accept4( listener, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK ); |
| 90 | +#else |
| 91 | + fd = accept( listener, NULL, NULL ); |
| 92 | +#endif |
| 93 | + |
| 94 | + if ( fd == -1 ) { |
| 95 | + perror( "Error accepting" ); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + if ( !HAVE_ACCEPT4 ) { |
| 100 | + int flags = fcntl( fd, F_GETFL ); |
| 101 | + if ( flags != -1 ) { |
| 102 | + fcntl( fd, F_SETFD, flags | FD_CLOEXEC | O_NONBLOCK ); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + cli = new_client_data( fd ); |
| 107 | + if ( !cli ) { |
| 108 | + perror( "Couldn't allocate the client data! :(" ); |
| 109 | + close( fd ); |
| 110 | + return; |
| 111 | + } |
| 112 | + open_sockets++; |
| 113 | + |
| 114 | + event_set( &cli->ev, fd, EV_READ, on_client, cli ); |
| 115 | + event_add( &cli->ev, NULL ); /* First query comes from client */ |
| 116 | +} |
| 117 | + |
| 118 | +void on_client(int fd, short type, void* arg) /* prototype */ |
| 119 | +{ |
| 120 | + int n; |
| 121 | + char *line; |
| 122 | + struct client_data* cli_data = arg; |
| 123 | + |
| 124 | + if ( type == EV_TIMEOUT ) { |
| 125 | + process_timeout( &cli_data->client_locks ); |
| 126 | + return; |
| 127 | + } |
| 128 | + |
| 129 | + n = read_client_line( fd, cli_data, &line ); |
| 130 | + |
| 131 | + if ( n < 0 ) { |
| 132 | + /* Client disconnected */ |
| 133 | + event_del( &cli_data->ev); |
| 134 | + free_client_data( cli_data ); |
| 135 | + close( fd ); |
| 136 | + open_sockets--; |
| 137 | + if ( !open_sockets ) { |
| 138 | + end( 0 ); |
| 139 | + } |
| 140 | + } else { |
| 141 | + while ( line ) { |
| 142 | + send_client( &cli_data->client_locks, process_line(cli_data, line, n) ); |
| 143 | + n = recover_client_buffer(cli_data, n, &line); |
| 144 | + } |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | +static void end(int signal) /* prototype */ |
| 149 | +{ |
| 150 | + close( 0 ); |
| 151 | + /* TODO: Close gracefully all connections */ |
| 152 | + event_loopbreak(); |
| 153 | +} |
| 154 | + |
| 155 | +static void graceful(int signal) |
| 156 | +{ |
| 157 | + pid_t p = fork(); |
| 158 | + if ( p == -1 ) { |
| 159 | + perror( "Can't fork" ); |
| 160 | + return; |
| 161 | + } |
| 162 | + |
| 163 | + if ( p ) { |
| 164 | + /* Stop listening connections */ |
| 165 | + close( 0 ); |
| 166 | + event_del( &listener_ev ); |
| 167 | + open_sockets--; |
| 168 | + if ( !open_sockets ) { |
| 169 | + /* We have no clients attached. Exit here. |
| 170 | + * Note: There is a race condition here. |
| 171 | + */ |
| 172 | + end( 0 ); |
| 173 | + } |
| 174 | + } else { |
| 175 | + execl( "/proc/self/exe", "poolcounterd", NULL ); |
| 176 | + exit( 1 ); |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +void setup_signals() /* prototype */ |
| 181 | +{ |
| 182 | + struct sigaction sa; |
| 183 | + sa.sa_flags = SA_RESTART; |
| 184 | + sigfillset( &sa.sa_mask ); |
| 185 | + |
| 186 | + sa.sa_handler = SIG_IGN; |
| 187 | + sigaction( SIGPIPE, &sa, NULL ); |
| 188 | + sigaction( SIGCHLD, &sa, NULL ); |
| 189 | + |
| 190 | + sa.sa_handler = end; |
| 191 | + sigaction( SIGINT, &sa, NULL ); |
| 192 | + sigaction( SIGTERM, &sa, NULL ); |
| 193 | + |
| 194 | + sa.sa_handler = graceful; |
| 195 | + sigaction( SIGUSR1, &sa, NULL ); |
| 196 | + |
| 197 | + /* Reset the process mask. It seems affected after one fork + execv in the signal handler */ |
| 198 | + sigemptyset( &sa.sa_mask ); |
| 199 | + sigprocmask( SIG_SETMASK, &sa.sa_mask, NULL ); |
| 200 | +} |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/main.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 201 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/client_data.c |
— | — | @@ -0,0 +1,104 @@ |
| 2 | +#include <stddef.h> |
| 3 | +#include <errno.h> |
| 4 | +#include <string.h> |
| 5 | +#include <sys/types.h> |
| 6 | +#include <sys/socket.h> |
| 7 | +#include <malloc.h> |
| 8 | +#include "client_data.h" |
| 9 | +#include "locks.h" |
| 10 | + |
| 11 | +struct client_data* new_client_data(int fd) { |
| 12 | + struct client_data* cd; |
| 13 | + cd = malloc( sizeof( *cd ) ); |
| 14 | + cd->used_buffer = 0; |
| 15 | + init_lock( &cd->client_locks ); |
| 16 | + cd->fd = fd; |
| 17 | + return cd; |
| 18 | +} |
| 19 | + |
| 20 | +void free_client_data(struct client_data* cli_data) { |
| 21 | + finish_lock( &cli_data->client_locks ); |
| 22 | + free( cli_data ); |
| 23 | +} |
| 24 | + |
| 25 | +/** |
| 26 | + * Read data from the client |
| 27 | + * If we filled a line, return the line length, and point to it in *line. |
| 28 | + * If a line is not available, *line will point to NULL. |
| 29 | + * Return -1 or -2 if the socket was closed (gracefully / erroneusly) |
| 30 | + * Line separator is \n. |
| 31 | + * Returned lines end in \0 with \n stripped. |
| 32 | + * Incomplete lines are not returned on close. |
| 33 | + */ |
| 34 | +int read_client_line(int fd, struct client_data* cli_data, char** line) { |
| 35 | + int n, i; |
| 36 | + |
| 37 | + *line = NULL; |
| 38 | + n = recv( fd, cli_data->buffer + cli_data->used_buffer, sizeof( cli_data->buffer ) - cli_data->used_buffer, 0 ); |
| 39 | + if ( n == 0 ) { |
| 40 | + return -1; |
| 41 | + } |
| 42 | + if ( n == -1 ) { |
| 43 | + if (errno == EAGAIN) { |
| 44 | + /* This shouldn't happen... */ |
| 45 | + return 0; |
| 46 | + } else { |
| 47 | + return -2; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + for ( i=cli_data->used_buffer; i < cli_data->used_buffer+n; i++ ) { |
| 52 | + if ( cli_data->buffer[i] == '\n' ) { |
| 53 | + cli_data->buffer[i] = '\0'; |
| 54 | + *line = cli_data->buffer; |
| 55 | + return i; |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + /* Wait for the rest of the line */ |
| 60 | + event_add( &cli_data->ev, NULL ); |
| 61 | + return 0; |
| 62 | +} |
| 63 | + |
| 64 | +/* Recover the space from the buffer which has been read, return another line if available */ |
| 65 | +int recover_client_buffer(struct client_data* cli_data, int len, char** line) { |
| 66 | + int i; |
| 67 | + *line = 0; |
| 68 | + if ( len >= cli_data->used_buffer ) { |
| 69 | + /* This is a query-response protocol. This should be *always* the case */ |
| 70 | + cli_data->used_buffer = 0; |
| 71 | + return 0; |
| 72 | + } |
| 73 | + |
| 74 | + /* Nonetheless handle the other case */ |
| 75 | + memmove(cli_data->buffer, cli_data->buffer + len, cli_data->used_buffer - len); |
| 76 | + cli_data->used_buffer -= len; |
| 77 | + |
| 78 | + for ( i=0; i < cli_data->used_buffer; i++ ) { |
| 79 | + if ( cli_data->buffer[i] == '\n' ) { |
| 80 | + cli_data->buffer[i] = '\0'; |
| 81 | + *line = cli_data->buffer; |
| 82 | + return i; |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + return 0; |
| 87 | +} |
| 88 | + |
| 89 | +/* Sends the message msg to the other side, or nothing if msg is NULL |
| 90 | + * Since the message are short, we optimistically consider that they |
| 91 | + * will always fit and never block (note O_NONBLOCK is set). |
| 92 | + */ |
| 93 | +void send_client(struct locks* l, const char* msg) { |
| 94 | + struct client_data* cli_data; |
| 95 | + if ( !msg ) return; |
| 96 | + |
| 97 | + cli_data = CLIENT_DATA_FROM_LOCKS(l); |
| 98 | + size_t len = strlen(msg); |
| 99 | + |
| 100 | + if ( send( cli_data->fd, msg, len, 0) != len ) { |
| 101 | + perror( "Something failed sending message" ); |
| 102 | + } |
| 103 | + /* Wait for answer */ |
| 104 | + event_add( &cli_data->ev, NULL ); |
| 105 | +} |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/client_data.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 106 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/hash.h |
— | — | @@ -0,0 +1,15 @@ |
| 2 | +#ifndef HASH_H |
| 3 | +#define HASH_H |
| 4 | + |
| 5 | +#ifdef __cplusplus |
| 6 | +extern "C" { |
| 7 | +#endif |
| 8 | + |
| 9 | +uint32_t hash(const void *key, size_t length, const uint32_t initval); |
| 10 | + |
| 11 | +#ifdef __cplusplus |
| 12 | +} |
| 13 | +#endif |
| 14 | + |
| 15 | +#endif /* HASH_H */ |
| 16 | + |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/hash.h |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 17 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/locks.c |
— | — | @@ -0,0 +1,276 @@ |
| 2 | +#define _XOPEN_SOURCE 500 |
| 3 | +#include <string.h> |
| 4 | +#include <stdlib.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include "locks.h" |
| 7 | +#include "hash.h" |
| 8 | +#include "client_data.h" |
| 9 | + |
| 10 | +void init_lock(struct locks* l) { |
| 11 | + l->state = UNLOCKED; |
| 12 | +} |
| 13 | + |
| 14 | +void finish_lock(struct locks* l) { |
| 15 | + if (l->state != UNLOCKED) { |
| 16 | + remove_client_lock(l, 0); |
| 17 | + } |
| 18 | +} |
| 19 | + |
| 20 | +static struct hashtable* primary_hashtable; |
| 21 | + |
| 22 | +/* These defines are the same in memcached */ |
| 23 | +#define hashsize(n) ((uint32_t)1<<(n)) |
| 24 | +#define hashmask(n) (hashsize(n)-1) |
| 25 | + |
| 26 | +#define DOUBLE_LLIST_INIT(this) do { (this).prev = (this).next = &(this); } while (0) |
| 27 | +#define DOUBLE_LLIST_DEL(this) do { (this)->prev->next = (this)->next; (this)->next->prev = (this)->prev; } while (0) |
| 28 | +#define DOUBLE_LLIST_ADD(parent,child) do { (child)->prev = (parent)->prev; (child)->next = (child)->prev->next /* parent */; (parent)->prev->next = (child); (parent)->prev = (child); } while(0); |
| 29 | + |
| 30 | +/* Converts a numeric text into an unsigned integer. |
| 31 | + * Returns 0 if it's a NULL pointer or not a natural. |
| 32 | + */ |
| 33 | +unsigned atou(char const* astext) { |
| 34 | + int num = 0; |
| 35 | + if (!astext) return 0; |
| 36 | + |
| 37 | + while ( *astext ) { |
| 38 | + if ( *astext < '0' ) return 0; |
| 39 | + if ( *astext > '9' ) return 0; |
| 40 | + num = num * 10 + *astext - '0'; |
| 41 | + astext++; |
| 42 | + } |
| 43 | + return num; |
| 44 | +} |
| 45 | + |
| 46 | +char* process_line(struct client_data* cli_data, char* line, int line_len) { |
| 47 | + struct locks* l = &cli_data->client_locks; |
| 48 | + if (line_len > 0 && line[line_len-1] == '\r') { |
| 49 | + line_len--; |
| 50 | + line[line_len] = '\0'; |
| 51 | + } |
| 52 | + |
| 53 | + if ( !strncmp( line, "ACQ4ME ", 7 ) || !strncmp( line, "ACQ4ANY ", 8 ) ) { |
| 54 | + if ( l->state != UNLOCKED ) { |
| 55 | + return "LOCK_HELD\n"; |
| 56 | + } |
| 57 | + |
| 58 | + int for_anyone = line[6] != ' '; |
| 59 | + |
| 60 | + char* key = strtok( line + 7 + for_anyone, " " ); |
| 61 | + unsigned workers = atou( strtok(NULL, " ") ); |
| 62 | + unsigned maxqueue = atou( strtok(NULL, " ") ); |
| 63 | + unsigned timeout = atou( strtok(NULL, " ") ); |
| 64 | + |
| 65 | + if ( !key || !workers || !maxqueue || !timeout ) { |
| 66 | + return "ERROR BAD_SYNTAX\n"; |
| 67 | + } |
| 68 | + |
| 69 | + uint32_t hash_value = hash( key, strlen( key ), 0 ); |
| 70 | + struct PoolCounter* pCounter; |
| 71 | + pCounter = hashtable_find( primary_hashtable, hash_value, key ); |
| 72 | + if ( !pCounter ) { |
| 73 | + pCounter = malloc( sizeof( *pCounter ) ); |
| 74 | + if ( !pCounter ) { |
| 75 | + fprintf(stderr, "Out of memory\n"); |
| 76 | + return "ERROR OUT_OF_MEMORY\n"; |
| 77 | + } |
| 78 | + pCounter->htentry.key = strdup( key ); |
| 79 | + pCounter->htentry.key_hash = hash_value; |
| 80 | + pCounter->count = 0; |
| 81 | + pCounter->processing = 0; |
| 82 | + |
| 83 | + DOUBLE_LLIST_INIT( pCounter->working ); |
| 84 | + DOUBLE_LLIST_INIT( pCounter->for_them ); |
| 85 | + DOUBLE_LLIST_INIT( pCounter->for_anyone ); |
| 86 | + |
| 87 | + hashtable_insert( primary_hashtable, (struct hashtable_entry *) pCounter ); |
| 88 | + } |
| 89 | + |
| 90 | + if ( pCounter->count >= maxqueue ) |
| 91 | + return "QUEUE_FULL\n"; |
| 92 | + |
| 93 | + l->parent = pCounter; |
| 94 | + pCounter->count++; |
| 95 | + |
| 96 | + if ( pCounter->processing < workers ) { |
| 97 | + l->state = PROCESSING; |
| 98 | + pCounter->processing++; |
| 99 | + DOUBLE_LLIST_ADD( &pCounter->working, &l->siblings ); |
| 100 | + return "LOCKED\n"; |
| 101 | + } else { |
| 102 | + struct timeval wait_time; |
| 103 | + if ( for_anyone ) { |
| 104 | + l->state = WAIT_ANY; |
| 105 | + DOUBLE_LLIST_ADD( &pCounter->for_anyone, &l->siblings ); |
| 106 | + } else { |
| 107 | + l->state = WAITING; |
| 108 | + DOUBLE_LLIST_ADD( &pCounter->for_them, &l->siblings ); |
| 109 | + } |
| 110 | + |
| 111 | + wait_time.tv_sec = timeout; |
| 112 | + wait_time.tv_usec = 0; |
| 113 | + |
| 114 | + event_add( &cli_data->ev, &wait_time ); |
| 115 | + return NULL; |
| 116 | + } |
| 117 | + } else if ( !strncmp(line, "RELEASE", 7) ) { |
| 118 | + if ( l->state == UNLOCKED ) { |
| 119 | + return "NOT_LOCKED\n"; |
| 120 | + } else { |
| 121 | + remove_client_lock( l, 1 ); |
| 122 | + return "RELEASED\n"; |
| 123 | + } |
| 124 | + } else { |
| 125 | + return "ERROR BAD_COMMAND\n"; |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +void process_timeout(struct locks* l) { |
| 130 | + if ( ( l->state == WAIT_ANY ) || ( l->state == WAITING ) ) { |
| 131 | + send_client( l, "TIMEOUT\n" ); |
| 132 | + remove_client_lock( l, 0 ); |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +void remove_client_lock(struct locks* l, int wakeup_anyones) { |
| 137 | + DOUBLE_LLIST_DEL(&l->siblings); |
| 138 | + |
| 139 | + if ( wakeup_anyones ) { |
| 140 | + while ( l->parent->for_anyone.next != &l->parent->for_anyone ) { |
| 141 | + send_client( (void*)l->parent->for_anyone.next, "DONE\n" ); |
| 142 | + remove_client_lock( (void*)l->parent->for_anyone.next, 0 ); |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + if ( l->state == PROCESSING ) { |
| 147 | + /* One slot freed, wake up another worker */ |
| 148 | + |
| 149 | + /* Give priority to those which need to do it themselves, since |
| 150 | + * the anyones will benefit from it, too. |
| 151 | + * TODO: Prefer the first anyone if it's much older. |
| 152 | + */ |
| 153 | + struct locks* new_owner = NULL; |
| 154 | + if ( l->parent->for_them.next != &l->parent->for_them ) { |
| 155 | + /* The oldest waiting worker will be on next */ |
| 156 | + new_owner = (struct locks*) l->parent->for_them.next; |
| 157 | + } else if ( l->parent->for_anyone.next != &l->parent->for_anyone ) { |
| 158 | + new_owner = (struct locks*) l->parent->for_anyone.next; |
| 159 | + } |
| 160 | + |
| 161 | + if ( new_owner ) { |
| 162 | + DOUBLE_LLIST_DEL( &new_owner->siblings ); |
| 163 | + DOUBLE_LLIST_ADD( &l->parent->working, &new_owner->siblings ); |
| 164 | + send_client( new_owner, "LOCKED\n" ); |
| 165 | + new_owner->state = PROCESSING; |
| 166 | + } else { |
| 167 | + l->parent->processing--; |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + l->state = UNLOCKED; |
| 172 | + l->parent->count--; |
| 173 | + if ( !l->parent->count ) { |
| 174 | + hashtable_remove( l->parent->htentry.parent_hashtable, &l->parent->htentry ); |
| 175 | + free( l->parent->htentry.key ); |
| 176 | + free( l->parent ); |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +/* The code below is loosely based in those of memcached assoc.c */ |
| 181 | +struct hashtable { |
| 182 | + unsigned int hashpower; |
| 183 | + uint32_t items; |
| 184 | + struct hashtable* old_hashtable; |
| 185 | + struct double_linked_list hashentries[1]; |
| 186 | +}; |
| 187 | + |
| 188 | +void hashtable_init() { |
| 189 | + primary_hashtable = hashtable_create(16); |
| 190 | + if (! primary_hashtable) { |
| 191 | + fprintf( stderr, "Failed to init hashtable.\n" ); |
| 192 | + exit( EXIT_FAILURE ); |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +struct hashtable* hashtable_create(int hashpower) { |
| 197 | + struct hashtable* new_hashtable; |
| 198 | + new_hashtable = calloc( hashsize( hashpower ) + ( sizeof( struct hashtable ) - 1 ) / |
| 199 | + sizeof( new_hashtable->hashentries[0] ), sizeof( new_hashtable->hashentries[0] ) ); |
| 200 | + |
| 201 | + if ( !new_hashtable ) |
| 202 | + return NULL; |
| 203 | + |
| 204 | + new_hashtable->hashpower = hashpower; |
| 205 | + if ( new_hashtable->old_hashtable != NULL ) { |
| 206 | + int i; /* Zeroes are not NULL here... */ |
| 207 | + new_hashtable->old_hashtable = NULL; |
| 208 | + for ( i=0; i < hashsize( hashpower ); i++ ) { |
| 209 | + new_hashtable->hashentries[i].prev = new_hashtable->hashentries[i].next = NULL; |
| 210 | + } |
| 211 | + } |
| 212 | + return new_hashtable; |
| 213 | +} |
| 214 | + |
| 215 | +/** |
| 216 | + * Find an entry with the given key in the hash table. |
| 217 | + * NULL if not found. |
| 218 | + */ |
| 219 | +void* hashtable_find(struct hashtable* ht, uint32_t hash_value, const char* key) { |
| 220 | + struct hashtable_entry *begin, *cur; |
| 221 | + |
| 222 | + begin = (struct hashtable_entry*) &ht->hashentries[hash_value & hashmask(ht->hashpower)]; |
| 223 | + if (!begin->hashtable_siblings.next) return NULL; /* Empty bucket */ |
| 224 | + |
| 225 | + for (cur = (struct hashtable_entry*) begin->hashtable_siblings.next; cur != begin; |
| 226 | + cur = (struct hashtable_entry*)cur->hashtable_siblings.next) { |
| 227 | + |
| 228 | + if ( ( cur->key_hash == hash_value ) && ( !strcmp( key, cur->key ) ) ) { |
| 229 | + return cur; |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | + if ( ht->old_hashtable ) { |
| 234 | + if ( !ht->old_hashtable->items ) { |
| 235 | + /* Empty hash table */ |
| 236 | + free(ht->old_hashtable); |
| 237 | + ht->old_hashtable = NULL; |
| 238 | + return NULL; |
| 239 | + } |
| 240 | + |
| 241 | + return hashtable_find( ht->old_hashtable, hash_value, key ); |
| 242 | + } |
| 243 | + return NULL; |
| 244 | +} |
| 245 | + |
| 246 | +/** |
| 247 | + * Insert into the hash table an item known not to exist there. |
| 248 | + */ |
| 249 | +void hashtable_insert(struct hashtable* ht, struct hashtable_entry* htentry) { |
| 250 | + struct double_linked_list* begin; |
| 251 | + |
| 252 | + if (! ht->old_hashtable && ht->items >= (hashsize( ht->hashpower ) * 3) / 2) { |
| 253 | + /* Same growing condition as in memcached */ |
| 254 | + struct hashtable* new_ht; |
| 255 | + new_ht = hashtable_create( ht->hashpower + 1 ); |
| 256 | + if ( new_ht ) { |
| 257 | + new_ht->old_hashtable = ht; |
| 258 | + primary_hashtable = new_ht; |
| 259 | + ht = new_ht; |
| 260 | + } |
| 261 | + } |
| 262 | + |
| 263 | + begin = &ht->hashentries[ htentry->key_hash & hashmask( ht->hashpower ) ]; |
| 264 | + if ( !begin->next ) { DOUBLE_LLIST_INIT( *begin ); } |
| 265 | + DOUBLE_LLIST_ADD( begin, &htentry->hashtable_siblings ); |
| 266 | + htentry->parent_hashtable = ht; |
| 267 | + ht->items++; |
| 268 | +} |
| 269 | + |
| 270 | +/** |
| 271 | + * Remove this entry from this hash table. |
| 272 | + * Freeing the entry is the caller's responsability. |
| 273 | + */ |
| 274 | +void hashtable_remove(struct hashtable* ht, struct hashtable_entry* htentry) { |
| 275 | + DOUBLE_LLIST_DEL( &htentry->hashtable_siblings ); |
| 276 | + ht->items--; |
| 277 | +} |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/locks.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 278 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/Makefile |
— | — | @@ -0,0 +1,18 @@ |
| 2 | +CC=gcc |
| 3 | +DEFINES=-DENDIAN_BIG=0 -DENDIAN_LITTLE=1 -DHAVE_ACCEPT4=1 |
| 4 | +CFLAGS=-Wall $(DEFINES) |
| 5 | +OBJS=main.o client_data.o locks.o hash.o |
| 6 | +LINK=-levent |
| 7 | +HEADERS=prototypes.h client_data.h |
| 8 | + |
| 9 | +poolcounterd: $(OBJS) |
| 10 | + $(CC) $(LINK) $^ -o $@ |
| 11 | + |
| 12 | +%.o: %.c $(HEADERS) |
| 13 | + $(CC) -c $(CFLAGS) $< -o $@ |
| 14 | + |
| 15 | +prototypes.h: main.c |
| 16 | + sed -n 's/\/\* prototype \*\//;/p' $^ > $@ |
| 17 | + |
| 18 | +clean: |
| 19 | + rm -f *.o prototypes.h |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/Makefile |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 20 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/client_data.h |
— | — | @@ -0,0 +1,23 @@ |
| 2 | +typedef unsigned char u_char; /* needed by event.h */ |
| 3 | +#include <stddef.h> |
| 4 | +#include <event.h> |
| 5 | +#include "locks.h" |
| 6 | + |
| 7 | +struct client_data { |
| 8 | + struct event ev; |
| 9 | + int fd; |
| 10 | + size_t used_buffer; |
| 11 | + char buffer[1024]; |
| 12 | + |
| 13 | + struct locks client_locks; |
| 14 | +}; |
| 15 | + |
| 16 | +#define CLIENT_DATA_FROM_LOCKS(cli_lock_pointer) ((struct client_data*)(((char*)(cli_lock_pointer)) - offsetof(struct client_data,client_locks))) |
| 17 | + |
| 18 | +struct client_data* new_client_data(); |
| 19 | +void free_client_data(struct client_data* cli_data); |
| 20 | +int read_client_line(int fd, struct client_data* cli_data, char** line); |
| 21 | +int recover_client_buffer(struct client_data* cli_data, int len, char** line); |
| 22 | + |
| 23 | +#define PORT 7531 |
| 24 | +#define BACKLOG 20 |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/client_data.h |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 25 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/prototypes.h |
— | — | @@ -0,0 +1,5 @@ |
| 2 | +int listensocket(short int port) ; |
| 3 | +void on_connect(int listener, short type, void* arg) ; |
| 4 | +void on_client(int fd, short type, void* arg) ; |
| 5 | +static void end(int signal) ; |
| 6 | +void setup_signals() ; |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon/prototypes.h |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 7 | + native |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon |
___________________________________________________________________ |
Added: svn:ignore |
2 | 8 | + poolcounterd |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient_body.php |
— | — | @@ -0,0 +1,141 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class PoolCounter_ConnectionManager { |
| 5 | + var $hostNames; |
| 6 | + var $conns = array(); |
| 7 | + var $refCounts = array(); |
| 8 | + |
| 9 | + function __construct( $conf ) { |
| 10 | + $this->hostNames = $conf['servers']; |
| 11 | + $this->timeout = isset( $conf['timeout'] ) ? $conf['timeout'] : 0.1; |
| 12 | + if ( !count( $this->hostNames ) ) { |
| 13 | + throw new MWException( __METHOD__ . ': no servers configured' ); |
| 14 | + } |
| 15 | + } |
| 16 | + |
| 17 | + function get( $key ) { |
| 18 | + $hashes = array(); |
| 19 | + foreach ( $this->hostNames as $hostName ) { |
| 20 | + $hashes[$hostName] = md5( $hostName . $key ); |
| 21 | + } |
| 22 | + asort( $hashes ); |
| 23 | + $errno = $errstr = ''; |
| 24 | + foreach ( $hashes as $hostName => $hash ) { |
| 25 | + if ( isset( $this->conns[$hostName] ) ) { |
| 26 | + $this->refCounts[$hostName]++; |
| 27 | + return Status::newGood( $this->conns[$hostName] ); |
| 28 | + } |
| 29 | + $parts = explode( ':', $hostName, 2 ); |
| 30 | + if ( count( $parts ) < 2 ) { |
| 31 | + $parts[] = 7531; |
| 32 | + } |
| 33 | + wfSuppressWarnings(); |
| 34 | + $conn = fsockopen( $parts[0], $parts[1], $errno, $errstr, $this->timeout ); |
| 35 | + wfRestoreWarnings(); |
| 36 | + if ( $conn ) { |
| 37 | + break; |
| 38 | + } |
| 39 | + } |
| 40 | + if ( !$conn ) { |
| 41 | + return Status::newFatal( 'poolcounter-connection-error', $errstr ); |
| 42 | + } |
| 43 | + wfDebug( "Connected to pool counter server: $hostName\n" ); |
| 44 | + $this->conns[$hostName] = $conn; |
| 45 | + $this->refCounts[$hostName] = 1; |
| 46 | + return Status::newGood( $conn ); |
| 47 | + } |
| 48 | + |
| 49 | + function close( $conn ) { |
| 50 | + foreach ( $this->conns as $hostName => $otherConn ) { |
| 51 | + if ( $conn === $otherConn ) { |
| 52 | + if ( $this->refCounts[$hostName] ) { |
| 53 | + $this->refCounts[$hostName]--; |
| 54 | + } |
| 55 | + if ( !$this->refCounts[$hostName] ) { |
| 56 | + fclose( $conn ); |
| 57 | + unset( $this->conns[$hostName] ); |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +class PoolCounter_Client extends PoolCounter { |
| 65 | + private $conn; |
| 66 | + |
| 67 | + static private $manager; |
| 68 | + |
| 69 | + function __construct( $conf, $type, $key ) { |
| 70 | + parent::__construct( $conf, $type, $key ); |
| 71 | + if ( !self::$manager ) { |
| 72 | + global $wgPoolCountClientConf; |
| 73 | + self::$manager = new PoolCounter_ConnectionManager( $wgPoolCountClientConf ); |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + function getConn() { |
| 78 | + if ( !isset( $this->conn ) ) { |
| 79 | + $status = self::$manager->get( $this->key ); |
| 80 | + if ( !$status->isOK() ) { |
| 81 | + return $status; |
| 82 | + } |
| 83 | + $this->conn = $status->value; |
| 84 | + } |
| 85 | + return Status::newGood( $this->conn ); |
| 86 | + } |
| 87 | + |
| 88 | + function sendCommand( /*, ...*/ ) { |
| 89 | + $args = func_get_args(); |
| 90 | + $args = str_replace( ' ', '%20', $args ); |
| 91 | + $cmd = implode( ' ', $args ); |
| 92 | + $status = $this->getConn(); |
| 93 | + if ( !$status->isOK() ) { |
| 94 | + return $status; |
| 95 | + } |
| 96 | + $conn = $status->value; |
| 97 | + wfDebug( "Sending pool counter command: $cmd\n" ); |
| 98 | + if ( fwrite( $conn, "$cmd\n" ) === false ) { |
| 99 | + return Status::newFatal( 'poolcounter-write-error' ); |
| 100 | + } |
| 101 | + $response = fgets( $conn ); |
| 102 | + if ( $response === false ) { |
| 103 | + return Status::newFatal( 'poolcounter-read-error' ); |
| 104 | + } |
| 105 | + $response = rtrim( $response, "\r\n" ); |
| 106 | + wfDebug( "Got pool counter response: $response\n" ); |
| 107 | + $parts = explode( ' ', $response, 2 ); |
| 108 | + $responseType = $parts[0]; |
| 109 | + switch ( $responseType ) { |
| 110 | + case 'ERROR': |
| 111 | + $parts = explode( ' ', $parts[1], 2 ); |
| 112 | + $errorMsg = isset( $parts[1] ) ? $parts[1] : '(no message given)'; |
| 113 | + return Status::newFatal( 'poolcounter-remote-error', $errorMsg ); |
| 114 | + case 'LOCKED': |
| 115 | + case 'RELEASED': |
| 116 | + case 'DONE': |
| 117 | + case 'NOT_LOCKED': |
| 118 | + case 'QUEUE_FULL': |
| 119 | + case 'TIMEOUT': |
| 120 | + case 'LOCK_HELD': |
| 121 | + return Status::newGood( constant( "PoolCounter::$responseType" ) ); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + function acquireForMe() { |
| 126 | + return $this->sendCommand( 'ACQ4ME', $this->key, $this->workers, $this->maxqueue, $this->timeout ); |
| 127 | + } |
| 128 | + |
| 129 | + function acquireForAnyone() { |
| 130 | + return $this->sendCommand( 'ACQ4ANY', $this->key, $this->workers, $this->maxqueue, $this->timeout ); |
| 131 | + } |
| 132 | + |
| 133 | + function release() { |
| 134 | + $status = $this->sendCommand( 'RELEASE', $this->key ); |
| 135 | + |
| 136 | + if ( $this->conn ) { |
| 137 | + self::$manager->close( $this->conn ); |
| 138 | + $this->conn = null; |
| 139 | + } |
| 140 | + return $status; |
| 141 | + } |
| 142 | +} |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient_body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 143 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient.i18n.php |
— | — | @@ -0,0 +1,351 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension PoolCounterClient. |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$messages = array(); |
| 11 | + |
| 12 | +/** English |
| 13 | + * @author Tim Starling |
| 14 | + */ |
| 15 | +$messages['en'] = array( |
| 16 | + 'poolcounter-desc' => 'MediaWiki client for the pool counter daemon', |
| 17 | + 'poolcounter-connection-error' => 'Error connecting to pool counter server: $1', |
| 18 | + 'poolcounter-read-error' => 'Error reading from pool counter server', |
| 19 | + 'poolcounter-write-error' => 'Error writing to pool counter server', |
| 20 | + 'poolcounter-remote-error' => 'Pool counter server error: $1', |
| 21 | +); |
| 22 | + |
| 23 | +/** Message documentation (Message documentation) |
| 24 | + * @author Purodha |
| 25 | + * @author Siebrand |
| 26 | + */ |
| 27 | +$messages['qqq'] = array( |
| 28 | + 'poolcounter-desc' => '{{desc}} |
| 29 | + |
| 30 | +A pool counter keeps track of the running processes on a cluster of processors, and may or may not grant a job access to the processing pool. (Note, the word "counter" relates to the counter in a shop, bank, or hotel, not to the verb "to count")', |
| 31 | +); |
| 32 | + |
| 33 | +/** Arabic (العربية) |
| 34 | + * @author Meno25 |
| 35 | + */ |
| 36 | +$messages['ar'] = array( |
| 37 | + 'poolcounter-desc' => 'عميل ميدياويكي لمشرف مجموعة daemon poolcounter.py', |
| 38 | + 'poolcounter-connection-error' => 'خطأ توصيل إلى خادم مشرف المجموعة: $1', |
| 39 | + 'poolcounter-read-error' => 'خطأ قراءة من خادم مشرف المجموعة', |
| 40 | + 'poolcounter-write-error' => 'خطأ كتابة إلى خادم مشرف المجموعة', |
| 41 | + 'poolcounter-remote-error' => 'خطأ خادم مشرف المجموعة: $1', |
| 42 | +); |
| 43 | + |
| 44 | +/** Belarusian (Taraškievica orthography) (Беларуская (тарашкевіца)) |
| 45 | + * @author EugeneZelenko |
| 46 | + */ |
| 47 | +$messages['be-tarask'] = array( |
| 48 | + 'poolcounter-desc' => 'Кліент MediaWiki для лічыльніка poolcounter.py', |
| 49 | + 'poolcounter-connection-error' => 'Памылка далучэньня для сэрвэра лічыльніка: $1', |
| 50 | + 'poolcounter-read-error' => 'Памылка чытаньня з сэрвэра лічыльніка', |
| 51 | + 'poolcounter-write-error' => 'Памылка запісу на сэрвэр лічыльніка', |
| 52 | + 'poolcounter-remote-error' => 'Памылка сэрвэра лічыльніка: $1', |
| 53 | +); |
| 54 | + |
| 55 | +/** Breton (Brezhoneg) |
| 56 | + * @author Fulup |
| 57 | + */ |
| 58 | +$messages['br'] = array( |
| 59 | + 'poolcounter-desc' => 'Kliant evit MediaWiki eus diaoul kanastell strollad poolcounter.py', |
| 60 | + 'poolcounter-connection-error' => 'Fazi kevreañ ouzh servijer kanastell ar strollad : $1', |
| 61 | + 'poolcounter-read-error' => 'Fazi lenn a-berzh servijer kanastell ar strollad', |
| 62 | + 'poolcounter-write-error' => 'Fazi e-ser skrivañ war servijer kanastell ar strollad', |
| 63 | + 'poolcounter-remote-error' => 'Fazi servijer kanastell ar strollad : $1', |
| 64 | +); |
| 65 | + |
| 66 | +/** Bosnian (Bosanski) |
| 67 | + * @author CERminator |
| 68 | + */ |
| 69 | +$messages['bs'] = array( |
| 70 | + 'poolcounter-desc' => 'MediaWiki klijent daemon za pool brojač poolcounter.py', |
| 71 | + 'poolcounter-connection-error' => 'Greška pri povezivanju na server pool brojača: $1', |
| 72 | + 'poolcounter-read-error' => 'Greška pri čitanju sa servera pool brojača', |
| 73 | + 'poolcounter-write-error' => 'Greška pri pisanju na server pool brojača', |
| 74 | + 'poolcounter-remote-error' => 'Greška na serveru pool brojača: $1', |
| 75 | +); |
| 76 | + |
| 77 | +/** German (Deutsch) |
| 78 | + * @author Kghbln |
| 79 | + * @author Purodha |
| 80 | + */ |
| 81 | +$messages['de'] = array( |
| 82 | + 'poolcounter-desc' => 'Stellt einen Klienten für MediaWiki für den Hintergrundprozeß „poolcounter.py“ eines Computerclusters bereit', |
| 83 | + 'poolcounter-connection-error' => 'Fehler beim Verbinden zum Server $1, auf dem sich das Zählwerk des Computerclusters befindet', |
| 84 | + 'poolcounter-read-error' => 'Fehler beim Lesen vom Server, auf dem sich das Zählwerk des Computerclusters befindet', |
| 85 | + 'poolcounter-write-error' => 'Fehler beim Schreiben auf dem Server, auf dem sich das Zählwerk des Computerclusters befindet', |
| 86 | + 'poolcounter-remote-error' => 'Fehler beim Server $1, auf dem sich das Zählwerk des Computerclusters befindet', |
| 87 | +); |
| 88 | + |
| 89 | +/** Lower Sorbian (Dolnoserbski) |
| 90 | + * @author Michawiki |
| 91 | + */ |
| 92 | +$messages['dsb'] = array( |
| 93 | + 'poolcounter-desc' => 'Klient MediaWiki za demon pool counter', |
| 94 | + 'poolcounter-connection-error' => 'Zmólka pśi zwězowanju ze serwerom pool counter: $1', |
| 95 | + 'poolcounter-read-error' => 'Zmólka pśi cytanju ze serwera pool counter', |
| 96 | + 'poolcounter-write-error' => 'Zmólka pśi pisanju na serwer pool counter', |
| 97 | + 'poolcounter-remote-error' => 'Zmólka serwera pool counter: $1', |
| 98 | +); |
| 99 | + |
| 100 | +/** Greek (Ελληνικά) |
| 101 | + * @author Omnipaedista |
| 102 | + */ |
| 103 | +$messages['el'] = array( |
| 104 | + 'poolcounter-desc' => 'Πελάτης για τη MediaWiki του daemon του καταμετρητή ομαδοποίησης poolcounter.py', |
| 105 | + 'poolcounter-connection-error' => 'Σφάλμα κατά την σύνδεση με τον καταμετρητή ομαδοποίησης: $1', |
| 106 | + 'poolcounter-read-error' => 'Σφάλμα κατά την ανάγνωση του εξυπηρετητή του καταμετρητή ομαδοποίησης', |
| 107 | + 'poolcounter-write-error' => 'Σφάλμα κατά την εγγραφή στον εξυπηρετητή του καταμετρητή ομαδοποίησης', |
| 108 | + 'poolcounter-remote-error' => 'Σφάλμα του εξυπηρετητή του καταμετρητή ομαδοποίησης: $1', |
| 109 | +); |
| 110 | + |
| 111 | +/** Spanish (Español) |
| 112 | + * @author Crazymadlover |
| 113 | + * @author Translationista |
| 114 | + */ |
| 115 | +$messages['es'] = array( |
| 116 | + 'poolcounter-desc' => 'Cliente MediaWiki para demonio del contador de encuestas poolcounter.py', |
| 117 | + 'poolcounter-connection-error' => 'Error conectando al servidor contador de encuestas: $1', |
| 118 | + 'poolcounter-read-error' => 'Error leyendo del servidor contador de encuestas', |
| 119 | + 'poolcounter-write-error' => 'Error escribiendo al servidor contador de encuestas', |
| 120 | + 'poolcounter-remote-error' => 'Error del servidor contador de encuestas: $1', |
| 121 | +); |
| 122 | + |
| 123 | +/** French (Français) |
| 124 | + * @author IAlex |
| 125 | + * @author Urhixidur |
| 126 | + */ |
| 127 | +$messages['fr'] = array( |
| 128 | + 'poolcounter-desc' => 'Client pour MediaWiki du démon de compteur de groupement', |
| 129 | + 'poolcounter-connection-error' => 'Erreur lors de la connexion au compteur de groupement : $1', |
| 130 | + 'poolcounter-read-error' => 'Erreur lors de la lecture du serveur du compteur de groupement', |
| 131 | + 'poolcounter-write-error' => "Erreur lors de l'écriture sur le serveur du compteur de groupement", |
| 132 | + 'poolcounter-remote-error' => 'Erreur du serveur du compteur de groupement : $1', |
| 133 | +); |
| 134 | + |
| 135 | +/** Galician (Galego) |
| 136 | + * @author Toliño |
| 137 | + */ |
| 138 | +$messages['gl'] = array( |
| 139 | + 'poolcounter-desc' => 'Cliente para MediaWiki do servidor de contador de recursos comúns poolcounter.py', |
| 140 | + 'poolcounter-connection-error' => 'Erro na conexión co servidor de contador de recursos comúns: $1', |
| 141 | + 'poolcounter-read-error' => 'Erro na lectura do servidor de contador de recursos comúns', |
| 142 | + 'poolcounter-write-error' => 'Erro na escritura do servidor de contador de recursos comúns', |
| 143 | + 'poolcounter-remote-error' => 'Erro do servidor de contador de recursos comúns: $1', |
| 144 | +); |
| 145 | + |
| 146 | +/** Swiss German (Alemannisch) |
| 147 | + * @author Als-Holder |
| 148 | + */ |
| 149 | +$messages['gsw'] = array( |
| 150 | + 'poolcounter-desc' => 'MediaWiki-Client fir dr Poolcounter-Daemon', |
| 151 | + 'poolcounter-connection-error' => 'Fähler bim Verbinde zum Poolcounter-Server: $1', |
| 152 | + 'poolcounter-read-error' => 'Fähler bim Läse vum Poolcounter-Server', |
| 153 | + 'poolcounter-write-error' => 'Fähler bim Schrybe an Poolcounter-Server', |
| 154 | + 'poolcounter-remote-error' => 'Poolcounter-Server-Fähler: $1', |
| 155 | +); |
| 156 | + |
| 157 | +/** Upper Sorbian (Hornjoserbsce) |
| 158 | + * @author Michawiki |
| 159 | + */ |
| 160 | +$messages['hsb'] = array( |
| 161 | + 'poolcounter-desc' => 'Klient MediaWiki demona skupinskeho ličaka poolcounter.py', |
| 162 | + 'poolcounter-connection-error' => 'Zmylk při zwjazowanju ze serwerom skupinskeho ličaka: $1', |
| 163 | + 'poolcounter-read-error' => 'Zmylk při čitanju ze serwera skupinskeho ličaka', |
| 164 | + 'poolcounter-write-error' => 'Zmylk při pisanju na serwer skupinskeho ličaka', |
| 165 | + 'poolcounter-remote-error' => 'Zmylk serwera skupinskeho ličaka: $1', |
| 166 | +); |
| 167 | + |
| 168 | +/** Hungarian (Magyar) |
| 169 | + * @author Glanthor Reviol |
| 170 | + */ |
| 171 | +$messages['hu'] = array( |
| 172 | + 'poolcounter-desc' => 'MediaWiki kliens a poolcounter.py démonhoz', |
| 173 | + 'poolcounter-connection-error' => 'Hiba a pool counter szerverhez való kapcsolódáskor: $1', |
| 174 | + 'poolcounter-read-error' => 'Hiba a pool counter szerverről való olvasáskor', |
| 175 | + 'poolcounter-write-error' => 'Hiba a pool counter szerverre való íráskor', |
| 176 | + 'poolcounter-remote-error' => 'Pool counter szerver hiba: $1', |
| 177 | +); |
| 178 | + |
| 179 | +/** Interlingua (Interlingua) |
| 180 | + * @author McDutchie |
| 181 | + */ |
| 182 | +$messages['ia'] = array( |
| 183 | + 'poolcounter-desc' => 'Cliente MediaWiki pro le daemon contator de ressources commun poolcounter.py', |
| 184 | + 'poolcounter-connection-error' => 'Error durante le connexion al contator de ressources commun: $1', |
| 185 | + 'poolcounter-read-error' => 'Error durante le lectura del contator de ressources commun', |
| 186 | + 'poolcounter-write-error' => 'Error durante le scriptura al contator de ressources commun', |
| 187 | + 'poolcounter-remote-error' => 'Error del contator de ressources commun: $1', |
| 188 | +); |
| 189 | + |
| 190 | +/** Indonesian (Bahasa Indonesia) |
| 191 | + * @author Bennylin |
| 192 | + */ |
| 193 | +$messages['id'] = array( |
| 194 | + 'poolcounter-desc' => 'Klien MediaWiki untuk daemon poolcounter.py', |
| 195 | + 'poolcounter-connection-error' => 'Kesalahan pada saat berusaha menghubungi peladen pool counter: $1', |
| 196 | + 'poolcounter-read-error' => 'Kesalahan pada saat berusaha membaca peladen pool counter', |
| 197 | + 'poolcounter-write-error' => 'Kesalahan pada saat berusaha menulis peladen pool counter', |
| 198 | + 'poolcounter-remote-error' => 'Kesalahan peladen pool server: $1', |
| 199 | +); |
| 200 | + |
| 201 | +/** Japanese (日本語) |
| 202 | + * @author Fryed-peach |
| 203 | + */ |
| 204 | +$messages['ja'] = array( |
| 205 | + 'poolcounter-desc' => 'プールカウンター・デーモン poolcounter.py の MediaWiki クライアント', |
| 206 | + 'poolcounter-connection-error' => 'プールカウンター・サーバーへの接続中にエラー: $1', |
| 207 | + 'poolcounter-read-error' => 'プールカウンター・サーバーからの読み込み中にエラー', |
| 208 | + 'poolcounter-write-error' => 'プールカウンター・サーバーへの書き込み中にエラー', |
| 209 | + 'poolcounter-remote-error' => 'プールカウンター・サーバーのエラー: $1', |
| 210 | +); |
| 211 | + |
| 212 | +/** Colognian (Ripoarisch) |
| 213 | + * @author Purodha |
| 214 | + */ |
| 215 | +$messages['ksh'] = array( |
| 216 | + 'poolcounter-desc' => 'Ene MediaWiki <i lang="en">client</i> för dat Hengerjrondprojramm <code lang="en">poolcounter.py</code> för et Parraatshtonn vun enem Pöngel vun Prozessore ze verwallde', |
| 217 | + 'poolcounter-connection-error' => 'Beim Verbenge met däm ẞööver för et Parraatshtonn vun enem Pöngel vun Prozessore ze verwallde, es ene Fähler opjetrodde: $1', |
| 218 | + 'poolcounter-read-error' => 'Beim Lässe vum ẞööver för et Parraatshtonn vun enem Pöngel vun Prozessore ze verwallde, es ene Fähler opjetrodde.', |
| 219 | + 'poolcounter-write-error' => 'Beim Schriive noh däm ẞööver för et Parraatshtonn vun enem Pöngel vun Prozessore ze verwallde, es ene Fähler opjetrodde.', |
| 220 | + 'poolcounter-remote-error' => 'Dä ẞööver för et Parraatshtonn vun enem Pöngel vun Prozessore ze verwallde hät dä Fähler „$1“ jemeldt.', |
| 221 | +); |
| 222 | + |
| 223 | +/** Luxembourgish (Lëtzebuergesch) |
| 224 | + * @author Robby |
| 225 | + */ |
| 226 | +$messages['lb'] = array( |
| 227 | + 'poolcounter-desc' => 'Mediawiki-Client fir de Pool-Counter-Daemon', |
| 228 | + 'poolcounter-connection-error' => 'Feeler beim Verbanne mam Pool-Counter-Server: $1', |
| 229 | +); |
| 230 | + |
| 231 | +/** Macedonian (Македонски) |
| 232 | + * @author Bjankuloski06 |
| 233 | + */ |
| 234 | +$messages['mk'] = array( |
| 235 | + 'poolcounter-desc' => 'МедијаВики клиент за демонот на фондовскиот шалтер poolcounter.py', |
| 236 | + 'poolcounter-connection-error' => 'Грешка при поврзувањето со опслужувачот на фондовскиот шалтер: $1', |
| 237 | + 'poolcounter-read-error' => 'Грешка први читањето од опслужувачот на фондовскиот шалтер', |
| 238 | + 'poolcounter-write-error' => 'Грешка при запишувањето во опслужувачот на фондовскиот шалтер', |
| 239 | + 'poolcounter-remote-error' => 'Грешка во опслужувачот на фондовскиот шалтер: $1', |
| 240 | +); |
| 241 | + |
| 242 | +/** Dutch (Nederlands) |
| 243 | + * @author Siebrand |
| 244 | + */ |
| 245 | +$messages['nl'] = array( |
| 246 | + 'poolcounter-desc' => 'MediaWiki-client voor de poolcounter daemon', |
| 247 | + 'poolcounter-connection-error' => 'Fout bij het verbinden met de poolcounter server: $1', |
| 248 | + 'poolcounter-read-error' => 'Fout bij het lezen van de poolcounter server', |
| 249 | + 'poolcounter-write-error' => 'Fout bij het schrijven naar de poolcounter server', |
| 250 | + 'poolcounter-remote-error' => 'Poolcounter serverfout: $1', |
| 251 | +); |
| 252 | + |
| 253 | +/** Norwegian (bokmål) (Norsk (bokmål)) |
| 254 | + * @author Nghtwlkr |
| 255 | + */ |
| 256 | +$messages['no'] = array( |
| 257 | + 'poolcounter-desc' => 'MediaWiki-klient for sammenslutningsskrankenissen poolcounter.py', |
| 258 | + 'poolcounter-connection-error' => 'Feil ved tilkobling til sammenslutningsskranketjener: $1', |
| 259 | + 'poolcounter-read-error' => 'Feil ved lesing fra sammenslutningsskranketjener', |
| 260 | + 'poolcounter-write-error' => 'Feil ved skriving til sammenslutningsskranketjeneren', |
| 261 | + 'poolcounter-remote-error' => 'Sammenslutningsskranketjenerfeil: $1', |
| 262 | +); |
| 263 | + |
| 264 | +/** Occitan (Occitan) |
| 265 | + * @author Cedric31 |
| 266 | + */ |
| 267 | +$messages['oc'] = array( |
| 268 | + 'poolcounter-desc' => 'Client per MediaWiki del demon de comptador de gropament poolcounter.py', |
| 269 | + 'poolcounter-connection-error' => 'Error al moment de la connexion al comptador de gropament : $1', |
| 270 | + 'poolcounter-read-error' => 'Error al moment de la lectura del servidor de comptador de gropament', |
| 271 | + 'poolcounter-write-error' => "Error al moment de l'escritura sul servidor del comptador de gropament", |
| 272 | + 'poolcounter-remote-error' => 'Error del servidor de comptador de gropament : $1', |
| 273 | +); |
| 274 | + |
| 275 | +/** Polish (Polski) |
| 276 | + * @author Sp5uhe |
| 277 | + */ |
| 278 | +$messages['pl'] = array( |
| 279 | + 'poolcounter-desc' => 'Klient MediaWiki dla demona nadzorującego klaster poolcounter.py', |
| 280 | + 'poolcounter-connection-error' => 'Błąd podczas łączenia z serwerem nadzorującym klaster – $1', |
| 281 | + 'poolcounter-read-error' => 'Błąd odczytu z serwera nadzorującego klaster', |
| 282 | + 'poolcounter-write-error' => 'Błąd podczas zapisywania do serwera nadzorującego klaster', |
| 283 | + 'poolcounter-remote-error' => 'Błąd serwera nadzorującego klaster – $1', |
| 284 | +); |
| 285 | + |
| 286 | +/** Piedmontese (Piemontèis) |
| 287 | + * @author Borichèt |
| 288 | + * @author Dragonòt |
| 289 | + */ |
| 290 | +$messages['pms'] = array( |
| 291 | + 'poolcounter-desc' => "Client për MediaWiki dël demon ëd conteur d'argropament", |
| 292 | + 'poolcounter-connection-error' => 'Eror an colegandse al server ëd pool counter: $1', |
| 293 | + 'poolcounter-read-error' => 'Eror an lesend dal server ëd pool counter', |
| 294 | + 'poolcounter-write-error' => 'Eror an scrivend al server ëd pool counter', |
| 295 | + 'poolcounter-remote-error' => 'Eror dël server ëd pool counter: $1', |
| 296 | +); |
| 297 | + |
| 298 | +/** Portuguese (Português) |
| 299 | + * @author Hamilton Abreu |
| 300 | + */ |
| 301 | +$messages['pt'] = array( |
| 302 | + 'poolcounter-desc' => "Cliente MediaWiki para o ''pool counter daemon''", |
| 303 | + 'poolcounter-connection-error' => "Erro na ligação ao servidor ''pool counter'': $1", |
| 304 | + 'poolcounter-read-error' => "Erro ao ler o servidor ''pool counter''", |
| 305 | + 'poolcounter-write-error' => "Erro ao escrever no servidor ''pool counter''", |
| 306 | + 'poolcounter-remote-error' => "Erro do servidor ''pool counter'': $1", |
| 307 | +); |
| 308 | + |
| 309 | +/** Brazilian Portuguese (Português do Brasil) |
| 310 | + * @author Eduardo.mps |
| 311 | + */ |
| 312 | +$messages['pt-br'] = array( |
| 313 | + 'poolcounter-desc' => 'Cliente mediawiki para o pool counter daemon', |
| 314 | + 'poolcounter-connection-error' => 'Erro ao conectar ao servidor do pool counter: $1', |
| 315 | + 'poolcounter-read-error' => 'Erro ao ler do servidor do pool counter', |
| 316 | + 'poolcounter-write-error' => 'Erro ao escrever no servidor do pool counter', |
| 317 | + 'poolcounter-remote-error' => 'Erro no servidor do pool counter: $1', |
| 318 | +); |
| 319 | + |
| 320 | +/** Russian (Русский) |
| 321 | + * @author Александр Сигачёв |
| 322 | + */ |
| 323 | +$messages['ru'] = array( |
| 324 | + 'poolcounter-desc' => 'Клиент MediaWiki для демона счётчика пула poolcounter.py', |
| 325 | + 'poolcounter-connection-error' => 'Ошибка при подключении к серверу-счётчику пула: $1', |
| 326 | + 'poolcounter-read-error' => 'Ошибка чтения с сервера-счётчика пула', |
| 327 | + 'poolcounter-write-error' => 'Ошибка записи при обращении к серверу-счётчику пула', |
| 328 | + 'poolcounter-remote-error' => 'Ошибка сервера-счётчика пула: $1', |
| 329 | +); |
| 330 | + |
| 331 | +/** Slovak (Slovenčina) |
| 332 | + * @author Helix84 |
| 333 | + */ |
| 334 | +$messages['sk'] = array( |
| 335 | + 'poolcounter-desc' => 'Klient MediaWiki démona počítadla skupiny poolcounter.py', |
| 336 | + 'poolcounter-connection-error' => 'Chyba pri pripájaní na server počítadla skupiny: $1', |
| 337 | + 'poolcounter-read-error' => 'Chyba pri čítaní zo servera počítadla skupiny', |
| 338 | + 'poolcounter-write-error' => 'Chyba pri zapisovaní na server počítadla skupiny', |
| 339 | + 'poolcounter-remote-error' => 'Chyba servera počítadla skupiny: $1', |
| 340 | +); |
| 341 | + |
| 342 | +/** Tagalog (Tagalog) |
| 343 | + * @author AnakngAraw |
| 344 | + */ |
| 345 | +$messages['tl'] = array( |
| 346 | + 'poolcounter-desc' => 'Kliyente ng MediaWiki para sa pambilang ng lawa ng poolcounter.py ng daemon', |
| 347 | + 'poolcounter-connection-error' => 'Kamalian sa pagkunekta sa tagapaghain ng pambilang ng lawa: $1', |
| 348 | + 'poolcounter-read-error' => 'Maling pagbasa mula sa tagapaghain ng pambilang ng lawa', |
| 349 | + 'poolcounter-write-error' => 'Kamalian sa pagsulat sa tagapaghain ng pambilang ng lawa', |
| 350 | + 'poolcounter-remote-error' => 'Kamalian sa tagapaghain ng pambilang ng lawa: $1', |
| 351 | +); |
| 352 | + |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 353 | + native |
Index: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient.php |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * MediaWiki client for the pool counter daemon poolcounter.py. |
| 6 | + */ |
| 7 | + |
| 8 | +$wgExtensionCredits['other'][] = array( |
| 9 | + 'path' => __FILE__, |
| 10 | + 'name' => 'Pool Counter Client', |
| 11 | + 'author' => 'Tim Starling', |
| 12 | + 'descriptionmsg' => 'poolcounter-desc', |
| 13 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:PoolCounter', |
| 14 | +); |
| 15 | + |
| 16 | + |
| 17 | +/** |
| 18 | + * Configuration array for the connection manager. |
| 19 | + * Use $wgPoolCounterConf to configure the pools. |
| 20 | + */ |
| 21 | +$wgPoolCountClientConf = array( |
| 22 | + /** |
| 23 | + * Array of hostnames, or hostname:port. The default port is 7531. |
| 24 | + */ |
| 25 | + 'servers' => array( '127.0.0.1' ), |
| 26 | + |
| 27 | + /** |
| 28 | + * Connect timeout |
| 29 | + */ |
| 30 | + 'timeout' => 0.1, |
| 31 | +); |
| 32 | + |
| 33 | +/** |
| 34 | + * Sample pool configuration: |
| 35 | + * $wgPoolCounterConf = array( 'ArticleView' => array( |
| 36 | + * 'class' => 'PoolCounter_Client', |
| 37 | + * 'timeout' => 15, // wait timeout in seconds |
| 38 | + * 'workers' => 5, // maximum number of active threads in each pool |
| 39 | + * 'maxqueue' => 50, // maximum number of total threads in each pool |
| 40 | + * ) ); |
| 41 | + */ |
| 42 | + |
| 43 | +$dir = dirname( __FILE__ ) . '/'; |
| 44 | +$wgAutoloadClasses['PoolCounter_ConnectionManager'] |
| 45 | + = $wgAutoloadClasses['PoolCounter_Client'] |
| 46 | + = $dir . 'PoolCounterClient_body.php'; |
| 47 | +$wgExtensionMessagesFiles['PoolCounterClient'] = $dir . 'PoolCounterClient.i18n.php'; |
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/PoolCounterClient.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 48 | + native |