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 */
#include <stdarg.h>
#include <string.h>
#include <stdatomic.h>
#include "plan9.h"
#include "fmt.h"
#include "fmtdef.h"
enum
{
Maxfmt = 64
Maxfmt = 128
};
typedef struct Convfmt Convfmt;
struct Convfmt
{
int c;
volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
Fmts fmt;
};
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];
} fmtalloc;
static Convfmt knownfmt[] = {
' ', __flagfmt,
'#', __flagfmt,
'%', __percentfmt,
'\'', __flagfmt,
'+', __flagfmt,
',', __flagfmt,
'-', __flagfmt,
'C', __runefmt, /* Plan 9 addition */
'E', __efgfmt,
#ifndef PLAN9PORT
'F', __efgfmt, /* ANSI only */
#endif
'G', __efgfmt,
#ifndef PLAN9PORT
'L', __flagfmt, /* ANSI only */
#endif
'S', __runesfmt, /* Plan 9 addition */
'X', __ifmt,
'b', __ifmt, /* Plan 9 addition */
'c', __charfmt,
'd', __ifmt,
'e', __efgfmt,
'f', __efgfmt,
'g', __efgfmt,
'h', __flagfmt,
#ifndef PLAN9PORT
'i', __ifmt, /* ANSI only */
#endif
'l', __flagfmt,
'n', __countfmt,
'o', __ifmt,
'p', __ifmt,
'r', __errfmt,
's', __strfmt,
#ifdef PLAN9PORT
'u', __flagfmt,
#else
'u', __ifmt,
#endif
'x', __ifmt,
0, nil,
} fmtalloc = {
#ifdef PLAN9PORT
ATOMIC_VAR_INIT(27),
#else
ATOMIC_VAR_INIT(30),
#endif
{
{' ', __flagfmt},
{'#', __flagfmt},
{'%', __percentfmt},
{'\'', __flagfmt},
{'+', __flagfmt},
{',', __flagfmt},
{'-', __flagfmt},
{'C', __runefmt}, /* Plan 9 addition */
{'E', __efgfmt},
#ifndef PLAN9PORT
{'F', __efgfmt}, /* ANSI only */
#endif
{'G', __efgfmt},
#ifndef PLAN9PORT
{'L', __flagfmt}, /* ANSI only */
#endif
{'S', __runesfmt}, /* Plan 9 addition */
{'X', __ifmt},
{'b', __ifmt}, /* Plan 9 addition */
{'c', __charfmt},
{'d', __ifmt},
{'e', __efgfmt},
{'f', __efgfmt},
{'g', __efgfmt},
{'h', __flagfmt},
#ifndef PLAN9PORT
{'i', __ifmt}, /* ANSI only */
#endif
{'l', __flagfmt},
{'n', __countfmt},
{'o', __ifmt},
{'p', __ifmt},
{'r', __errfmt},
{'s', __strfmt},
#ifdef PLAN9PORT
{'u', __flagfmt},
#else
{'u', __ifmt},
#endif
{'x', __ifmt},
}
};
int (*fmtdoquote)(int);
/*
* __fmtwlock() must be set
* __fmtlock() must be set
*/
static int
__fmtinstall(int c, Fmts f)
{
Convfmt *p, *ep;
Convfmt *p;
int i;
if(c<=0 || c>=65536)
return -1;
if(!f)
f = __badfmt;
ep = &fmtalloc.fmt[fmtalloc.nfmt];
for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c)
break;
if(p == &fmtalloc.fmt[Maxfmt])
i = atomic_load(&fmtalloc.nfmt);
if(i == Maxfmt)
return -1;
p = &fmtalloc.fmt[i];
p->c = c;
p->fmt = f;
if(p == ep){ /* installing a new format character */
fmtalloc.nfmt++;
p->c = c;
}
atomic_store(&fmtalloc.nfmt, i+1);
return 0;
}
@ -106,43 +111,21 @@ fmtinstall(int c, int (*f)(Fmt*))
{
int ret;
__fmtwlock();
__fmtlock();
ret = __fmtinstall(c, f);
__fmtwunlock();
__fmtunlock();
return ret;
}
static Fmts
fmtfmt(int c)
{
Convfmt *p, *ep, *kp;
Convfmt *p, *ep;
/* conflict-free check - common case */
__fmtrlock();
ep = &fmtalloc.fmt[fmtalloc.nfmt];
for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c){
__fmtrunlock();
ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
for(p=ep; p-- > fmtalloc.fmt; )
if(p->c == c)
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;
}

View file

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

View file

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

View file

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