lib9: make formatting lock-free again

First use of <stdatomic.h>.
We will see if any supported systems don't have it yet.
(C11 was so last decade.)

Fixes #338.
This commit is contained in:
Russ Cox 2020-01-14 18:03:05 -05:00
parent d28913a9e6
commit 9505cd15a6
4 changed files with 82 additions and 123 deletions

View file

@ -1,102 +1,107 @@
/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <stdatomic.h>
#include "plan9.h" #include "plan9.h"
#include "fmt.h" #include "fmt.h"
#include "fmtdef.h" #include "fmtdef.h"
enum enum
{ {
Maxfmt = 64 Maxfmt = 128
}; };
typedef struct Convfmt Convfmt; typedef struct Convfmt Convfmt;
struct Convfmt struct Convfmt
{ {
int c; int c;
volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ Fmts fmt;
}; };
static struct static struct
{ {
/* lock by calling __fmtlock, __fmtunlock */ /*
int nfmt; * lock updates to fmt by calling __fmtlock, __fmtunlock.
* reads can start at nfmt and work backward without
* further locking. later fmtinstalls take priority over earlier
* ones because of the backwards loop.
* once installed, a format is never overwritten.
*/
_Atomic int nfmt;
Convfmt fmt[Maxfmt]; Convfmt fmt[Maxfmt];
} fmtalloc; } fmtalloc = {
#ifdef PLAN9PORT
static Convfmt knownfmt[] = { ATOMIC_VAR_INIT(27),
' ', __flagfmt, #else
'#', __flagfmt, ATOMIC_VAR_INIT(30),
'%', __percentfmt, #endif
'\'', __flagfmt, {
'+', __flagfmt, {' ', __flagfmt},
',', __flagfmt, {'#', __flagfmt},
'-', __flagfmt, {'%', __percentfmt},
'C', __runefmt, /* Plan 9 addition */ {'\'', __flagfmt},
'E', __efgfmt, {'+', __flagfmt},
#ifndef PLAN9PORT {',', __flagfmt},
'F', __efgfmt, /* ANSI only */ {'-', __flagfmt},
#endif {'C', __runefmt}, /* Plan 9 addition */
'G', __efgfmt, {'E', __efgfmt},
#ifndef PLAN9PORT #ifndef PLAN9PORT
'L', __flagfmt, /* ANSI only */ {'F', __efgfmt}, /* ANSI only */
#endif #endif
'S', __runesfmt, /* Plan 9 addition */ {'G', __efgfmt},
'X', __ifmt, #ifndef PLAN9PORT
'b', __ifmt, /* Plan 9 addition */ {'L', __flagfmt}, /* ANSI only */
'c', __charfmt, #endif
'd', __ifmt, {'S', __runesfmt}, /* Plan 9 addition */
'e', __efgfmt, {'X', __ifmt},
'f', __efgfmt, {'b', __ifmt}, /* Plan 9 addition */
'g', __efgfmt, {'c', __charfmt},
'h', __flagfmt, {'d', __ifmt},
#ifndef PLAN9PORT {'e', __efgfmt},
'i', __ifmt, /* ANSI only */ {'f', __efgfmt},
#endif {'g', __efgfmt},
'l', __flagfmt, {'h', __flagfmt},
'n', __countfmt, #ifndef PLAN9PORT
'o', __ifmt, {'i', __ifmt}, /* ANSI only */
'p', __ifmt, #endif
'r', __errfmt, {'l', __flagfmt},
's', __strfmt, {'n', __countfmt},
#ifdef PLAN9PORT {'o', __ifmt},
'u', __flagfmt, {'p', __ifmt},
#else {'r', __errfmt},
'u', __ifmt, {'s', __strfmt},
#endif #ifdef PLAN9PORT
'x', __ifmt, {'u', __flagfmt},
0, nil, #else
{'u', __ifmt},
#endif
{'x', __ifmt},
}
}; };
int (*fmtdoquote)(int); int (*fmtdoquote)(int);
/* /*
* __fmtwlock() must be set * __fmtlock() must be set
*/ */
static int static int
__fmtinstall(int c, Fmts f) __fmtinstall(int c, Fmts f)
{ {
Convfmt *p, *ep; Convfmt *p;
int i;
if(c<=0 || c>=65536) if(c<=0 || c>=65536)
return -1; return -1;
if(!f) if(!f)
f = __badfmt; f = __badfmt;
ep = &fmtalloc.fmt[fmtalloc.nfmt]; i = atomic_load(&fmtalloc.nfmt);
for(p=fmtalloc.fmt; p<ep; p++) if(i == Maxfmt)
if(p->c == c)
break;
if(p == &fmtalloc.fmt[Maxfmt])
return -1; return -1;
p = &fmtalloc.fmt[i];
p->c = c;
p->fmt = f; p->fmt = f;
if(p == ep){ /* installing a new format character */ atomic_store(&fmtalloc.nfmt, i+1);
fmtalloc.nfmt++;
p->c = c;
}
return 0; return 0;
} }
@ -106,43 +111,21 @@ fmtinstall(int c, int (*f)(Fmt*))
{ {
int ret; int ret;
__fmtwlock(); __fmtlock();
ret = __fmtinstall(c, f); ret = __fmtinstall(c, f);
__fmtwunlock(); __fmtunlock();
return ret; return ret;
} }
static Fmts static Fmts
fmtfmt(int c) fmtfmt(int c)
{ {
Convfmt *p, *ep, *kp; Convfmt *p, *ep;
/* conflict-free check - common case */ ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
__fmtrlock(); for(p=ep; p-- > fmtalloc.fmt; )
ep = &fmtalloc.fmt[fmtalloc.nfmt]; if(p->c == c)
for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c){
__fmtrunlock();
return p->fmt; return p->fmt;
}
__fmtrunlock();
/* is this a predefined format char? */
for(kp=knownfmt; kp->c; kp++){
if(kp->c == c){
__fmtwlock();
/* double-check fmtinstall didn't happen */
for(p=fmtalloc.fmt; p<ep; p++){
if(p->c == c){
__fmtwunlock();
return p->fmt;
}
}
__fmtinstall(kp->c, kp->fmt);
__fmtwunlock();
return kp->fmt;
}
}
return __badfmt; return __badfmt;
} }

View file

@ -36,10 +36,8 @@ void * __fmtflush(Fmt *f, void *t, int len);
int __fmtpad(Fmt *f, int n); int __fmtpad(Fmt *f, int n);
double __fmtpow10(int n); double __fmtpow10(int n);
int __fmtrcpy(Fmt *f, const void *vm, int n); int __fmtrcpy(Fmt *f, const void *vm, int n);
void __fmtrlock(void); void __fmtlock(void);
void __fmtrunlock(void); void __fmtunlock(void);
void __fmtwlock(void);
void __fmtwunlock(void);
int __ifmt(Fmt *f); int __ifmt(Fmt *f);
int __isInf(double d, int sign); int __isInf(double d, int sign);
int __isNaN(double d); int __isNaN(double d);

View file

@ -5,21 +5,11 @@
#include "fmtdef.h" #include "fmtdef.h"
void void
__fmtrlock(void) __fmtlock(void)
{ {
} }
void void
__fmtrunlock(void) __fmtunlock(void)
{
}
void
__fmtwlock(void)
{
}
void
__fmtwunlock(void)
{ {
} }

View file

@ -1,28 +1,16 @@
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
static RWLock fmtlock; static Lock fmtlock;
void void
__fmtrlock(void) __fmtlock(void)
{ {
rlock(&fmtlock); lock(&fmtlock);
} }
void void
__fmtrunlock(void) __fmtunlock(void)
{ {
runlock(&fmtlock); unlock(&fmtlock);
}
void
__fmtwlock(void)
{
wlock(&fmtlock);
}
void
__fmtwunlock(void)
{
wunlock(&fmtlock);
} }