r84001 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r84000‎ | r84001 | r84002 >
Date:06:36, 15 March 2011
Author:tstarling
Status:ok (Comments)
Tags:
Comment:
Copied PoolCounter extension from REL1_17.
Modified paths:
  • /branches/wmf/1.17wmf1/extensions/PoolCounter (added) (history)

Diff [purge]

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
1230 + native
Added: svn:executable
2231 + *
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
155 + 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
1434 + 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
1201 + 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
1106 + 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
117 + 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
1278 + 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
120 + 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
125 + 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
17 + native
Property changes on: branches/wmf/1.17wmf1/extensions/PoolCounter/daemon
___________________________________________________________________
Added: svn:ignore
28 + 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
1143 + 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
1353 + 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
148 + native

Comments

#Comment by Raymond (talk | contribs)   07:28, 27 April 2011

Has this deployed extension to be added to /trunk/tools/make-wmf-branch/default.conf too?

Status & tagging log