9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] [PATH] error()/waserror() for userspace
@ 2024-05-11 21:26 Jacob Moody
  2024-05-12 14:47 ` cinap_lenrek
  0 siblings, 1 reply; 2+ messages in thread
From: Jacob Moody @ 2024-05-11 21:26 UTC (permalink / raw)
  To: 9front

Hello,

Through some discussions I've been thinking about how to perhaps do better
process-local variables then our current privalloc(), I've been toying with
this idea for a while and think I've come up with a decent alternative.

This patch implements two new segments that a process can attach to
that have convenient properties. "Private" segments are zero'd on
rfork, no matter the RFMEM flag. "Stack" segments are exactly what
they sound like, they have the same semantics as the stack segment,
so they are always Copy-On-Write no matter the RFMEM flag.

What I have here is an error()/waserror() that uses these segments
to implement itself. It also has a libthread implementation so it
may be used transparently. This is to provide some idea of how this
type of interface can be used.

I am not entirely sure that this is the correct interface for code
like this so I welcome feedback. If this is an interface that we
like I can then start to work on the man pages and use of this
for other bits of code.


Thanks,
moody

diff 025a2d172ebfe36bc0da32f5712dd250916c73f1 uncommitted
--- /dev/null
+++ b/sys/include/error.h
@@ -1,0 +1,7 @@
+_Noreturn void	error(char*, ...);
+_Noreturn void	nexterror(void);
+void		poperror(void);
+jmp_buf*	pusherrlab(void);
+
+#define waserror() (setjmp(*pusherrlab()))
+#pragma	varargck	argpos	error	1
--- a/sys/include/libc.h
+++ b/sys/include/libc.h
@@ -116,6 +116,11 @@
 extern	void*	malloctopoolblock(void*);

 /*
+ * privmalloc
+ */
+extern	void*	privmalloc(ulong);
+
+/*
  * print routines
  */
 typedef struct Fmt	Fmt;
--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -143,7 +143,7 @@
 };

 /* Segment type from portdat.h */
-static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed", "Sticky" };
+static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed", "Sticky", "Private" };

 /*
  * Qids are, in path:
--- a/sys/src/9/port/devswap.c
+++ b/sys/src/9/port/devswap.c
@@ -202,6 +202,7 @@
 				case SG_BSS:
 				case SG_STACK:
 				case SG_SHARED:
+				case SG_PRIVATE:
 					pageout(p, s);
 					break;
 				}
@@ -302,6 +303,7 @@
 	case SG_BSS:
 	case SG_STACK:
 	case SG_SHARED:
+	case SG_PRIVATE:
 		if(ioptr >= conf.nswppo)
 			break;

--- a/sys/src/9/port/fault.c
+++ b/sys/src/9/port/fault.c
@@ -172,6 +172,7 @@
 	case SG_BSS:
 	case SG_SHARED:			/* Zero fill on demand */
 	case SG_STACK:
+	case SG_PRIVATE:
 		if(*pg == nil) {
 			new = newpage(1, &s, addr);
 			if(s == nil)
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -372,15 +372,16 @@
 /* Segment types */
 enum
 {
-	SG_TYPE		= 07,		/* Mask type of segment */
-	SG_TEXT		= 00,
-	SG_DATA		= 01,
-	SG_BSS		= 02,
-	SG_STACK	= 03,
-	SG_SHARED	= 04,
-	SG_PHYSICAL	= 05,
-	SG_FIXED	= 06,
-	SG_STICKY	= 07,
+	SG_TYPE		= 0xf,		/* Mask type of segment */
+	SG_TEXT		= 0,
+	SG_DATA		= 1,
+	SG_BSS		= 2,
+	SG_STACK	= 3,
+	SG_SHARED	= 4,
+	SG_PHYSICAL	= 5,
+	SG_FIXED	= 6,
+	SG_STICKY	= 7,
+	SG_PRIVATE	= 8,

 	SG_RONLY	= 0040,		/* Segment is read only */
 	SG_CEXEC	= 0100,		/* Detach at exec */
--- a/sys/src/9/port/segment.c
+++ b/sys/src/9/port/segment.c
@@ -9,9 +9,11 @@
  * Attachable segment types
  */
 static Physseg physseg[10] = {
-	{ SG_SHARED,	"shared",	0,	SEGMAXSIZE	},
-	{ SG_BSS,	"memory",	0,	SEGMAXSIZE	},
-	{ 0,		0,		0,	0		},
+	{ SG_SHARED,		"shared",	0,	SEGMAXSIZE	},
+	{ SG_BSS,		"memory",	0,	SEGMAXSIZE	},
+	{ SG_STACK|SG_CEXEC,	"stack",	0,	SEGMAXSIZE	},
+	{ SG_PRIVATE|SG_CEXEC,	"private",	0,	SEGMAXSIZE	},
+	{ 0,			0,		0,	0		},
 };

 static Lock physseglock;
@@ -169,6 +171,10 @@
 	case SG_STICKY:
 		goto sameseg;

+	case SG_PRIVATE:
+		n = newseg(s->type, s->base, s->size);
+		goto nocopy;
+
 	case SG_STACK:
 		n = newseg(s->type, s->base, s->size);
 		break;
@@ -203,6 +209,7 @@
 			n->map[i] = ptecpy(pte);

 	n->flushme = s->flushme;
+nocopy:
 	if(s->ref > 1)
 		procflushseg(s);
 	qunlock(s);
--- /dev/null
+++ b/sys/src/libc/port/err.c
@@ -1,0 +1,64 @@
+#include <u.h>
+#include <libc.h>
+
+typedef struct Errlab Errlab;
+struct Errlab {
+	int n;
+	jmp_buf s[32];
+};
+
+static Errlab*
+errorchk(void)
+{
+	static Errlab *e = nil;
+	static Lock l = {0};
+
+	if(e == nil){
+		lock(&l);
+		if(e == nil)
+			e = privmalloc(sizeof(Errlab));
+		unlock(&l);
+	}
+	return e;
+}
+
+_Noreturn void
+nexterror(void)
+{
+	Errlab *e;
+
+	e = errorchk();
+	if(e->n <= 0)
+		abort();
+	longjmp(e->s[--e->n], 1);
+}
+
+_Noreturn void
+error(char *fmt, ...)
+{
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+ERRMAX, fmt, arg);
+	va_end(arg);
+	errstr(buf, ERRMAX);
+
+	nexterror();
+}
+
+void
+poperror(void)
+{
+	if(--errorchk()->n < 0)
+		abort();
+}
+
+jmp_buf*
+pusherrlab(void)
+{
+	Errlab *e;
+
+	e = errorchk();
+	return e->s + e->n++;
+}
--- a/sys/src/libc/port/mkfile
+++ b/sys/src/libc/port/mkfile
@@ -23,6 +23,7 @@
 	cycles.c\
 	date.c\
 	encodefmt.c\
+	err.c\
 	execl.c\
 	exits.c\
 	exp.c\
@@ -57,6 +58,7 @@
 	pool.c\
 	pow.c\
 	pow10.c\
+	privmalloc.c\
 	profile.c\
 	qsort.c\
 	quote.c\
--- /dev/null
+++ b/sys/src/libc/port/privmalloc.c
@@ -1,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+enum {
+	Maxsize = 1024*1024,
+};
+
+static Lock alloc;
+static uchar *segstart, *segpos, *segend;
+
+void*
+privmalloc(ulong n)
+{
+	void *p;
+
+	lock(&alloc);
+	if(segstart == nil){
+		segstart = segpos = segattach(0, "private", 0, Maxsize);
+		if(segstart == nil)
+			abort();
+		segend = segstart + Maxsize;
+	}
+	if(segpos + n < segend){
+		p = segpos;
+		segpos += n;
+	} else
+		p = nil;
+	unlock(&alloc);
+	return p;
+}
--- /dev/null
+++ b/sys/src/libc/test/err.c
@@ -1,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <error.h>
+
+int n;
+
+void
+singleproc(void)
+{
+	n = 0;
+
+	if(waserror()){
+		if(n != 2)
+			sysfatal("missed label");
+		return;
+	}
+
+	if(waserror()){
+		n++;
+		nexterror();
+	}
+
+	n++;
+	error("blah");
+}
+
+int n2;
+Lock l;
+
+void
+multiproc(void)
+{
+
+	if(waserror()){
+		if(n2 != 1)
+			sysfatal("missed multiproc label");
+		waitpid();
+		return;
+	}
+	/*
+	 * Have the child add a new error label first
+	 * and check that it hasn't impacted the parents
+	 * error stack.
+	 */
+	lock(&l);
+	switch(rfork(RFMEM|RFPROC)){
+	case -1:
+		sysfatal("fork fail: %r");
+	default:
+		lock(&l);
+		n2++;
+		error("blah");
+	case 0:
+		if(waserror())
+			exits(nil);
+		unlock(&l);
+		error("blah");
+	}
+}
+
+void
+main(int, char**)
+{
+	singleproc();
+	multiproc();
+	exits(nil);
+}
--- a/sys/src/libc/test/mkfile
+++ b/sys/src/libc/test/mkfile
@@ -2,6 +2,7 @@

 TEST=\
 	date\
+	err\
 	pow\
 	runebreak\
 	runenorm\
--- /dev/null
+++ b/sys/src/libthread/err.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "threadimpl.h"
+
+static Errlab*
+errorchk(void)
+{
+	return &_threadgetproc()->thread->errlab;
+}
+
+_Noreturn void
+nexterror(void)
+{
+	Errlab *e;
+
+	e = errorchk();
+	if(e->n <= 0)
+		abort();
+	longjmp(e->s[--e->n], 1);
+}
+
+_Noreturn void
+error(char *fmt, ...)
+{
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+ERRMAX, fmt, arg);
+	va_end(arg);
+	errstr(buf, ERRMAX);
+
+	nexterror();
+}
+
+void
+poperror(void)
+{
+	if(--errorchk()->n < 0)
+		abort();
+}
+
+jmp_buf*
+pusherrlab(void)
+{
+	Errlab *e;
+
+	e = errorchk();
+	return e->s + e->n++;
+}
--- a/sys/src/libthread/mkfile
+++ b/sys/src/libthread/mkfile
@@ -8,6 +8,7 @@
 	chanprint.$O\
 	create.$O\
 	debug.$O\
+	err.$O\
 	exec.$O\
 	exit.$O\
 	id.$O\
--- /dev/null
+++ b/sys/src/libthread/test/err.c
@@ -1,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <error.h>
+
+static int b;
+
+void
+threadb(void*)
+{
+	if(waserror()){
+		if(b != 2)
+			sysfatal("missed label");
+		threadexits(nil);
+	}
+
+	if(waserror()){
+		b++;
+		nexterror();
+	}
+
+	b++;
+	error("blah");
+}
+
+static int a;
+
+void
+threada(void*)
+{
+	if(waserror()){
+		if(a != 2)
+			sysfatal("missed label");
+		threadexits(nil);
+	}
+
+	if(waserror()){
+		a++;
+		nexterror();
+	}
+
+	a++;
+	error("blah");
+}
+
+static int n;
+
+void
+threadmain(int, char**)
+{
+	n = 0;
+	if(waserror()){
+		if(n != 2)
+			sysfatal("missed label");
+		exits(nil);
+	}
+
+	if(waserror()){
+		n++;
+		nexterror();
+	}
+
+	threadcreate(threada, nil, 1024);
+	threadcreate(threadb, nil, 1024);
+	n++;
+	error("fail");
+}
--- /dev/null
+++ b/sys/src/libthread/test/mkfile
@@ -1,0 +1,6 @@
+</$objtype/mkfile
+
+TEST=\
+	err\
+
+</sys/src/cmd/mktest
--- a/sys/src/libthread/threadimpl.h
+++ b/sys/src/libthread/threadimpl.h
@@ -26,6 +26,7 @@
 typedef struct Execargs	Execargs;
 typedef struct Proc		Proc;
 typedef struct Iocall	Iocall;
+typedef struct Errlab	Errlab;

 /* must match list in sched.c */
 typedef enum
@@ -62,6 +63,11 @@
 	Thread	**tail;
 };

+struct Errlab {
+	int n;
+	jmp_buf s[32];
+};
+
 struct Thread
 {
 	Lock		lock;		/* protects thread data structure */
@@ -90,6 +96,7 @@
 	Chanstate	chan;		/* which channel operation is current */
 	Alt		*alt;		/* pointer to current alt structure (debugging) */

+	Errlab		errlab;		/* waserror()/error() jmp labels */
 	void*		udata;		/* User per-thread data pointer */
 };



^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [9front] [PATH] error()/waserror() for userspace
  2024-05-11 21:26 [9front] [PATH] error()/waserror() for userspace Jacob Moody
@ 2024-05-12 14:47 ` cinap_lenrek
  0 siblings, 0 replies; 2+ messages in thread
From: cinap_lenrek @ 2024-05-12 14:47 UTC (permalink / raw)
  To: 9front

Some notes and questions:

1)
privmalloc() should probably round the size up to 8 bytes,
so we maintain struct alignment.

2)
We need a manpage for privmalloc() describing the zeroing
semantics on fork().

3)
Should privmalloc() nil instead of abort()? arguments pro/con?

4)
maybe should introduce privmalloc() as a separate commit.
this has alot of impact as you'd want to implement nsec()
and atexit() using this new mechanism. doing that first
is a way to ensure that the design is sound. also, we
have to think about backwards compaty. if you run a old
kernel that has no "private" segment trype. should atexit()
fall back on global pid tagged array? ect.

5)
theres no range check in pusherrlab(). It just silentely
corrupts memory.

6)
Maybe call Errlab to "Errlabs" because it represents multiple
error labels. In the kernel, we have Label struct representing
a single "jumpbuf" and it is put in the Proc struct as Label
errlab[NERR].

7)
Is 32 labels good enougth? For libthread, we could even
just allocate the labels dynamically as we control the
whole thread. a jmp_buf costs you like 16 bytes, which
means the thread gets bloated by 512 bytes, even if
we never use error().

8)
For porting programs using privmalloc(), how'd we be able
to emulate these semantics? Is it possible?

9)
Should we provide a noteerror(ureg) function,
so you can raise an error from a note handler?

--
cinap

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2024-05-12 14:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-11 21:26 [9front] [PATH] error()/waserror() for userspace Jacob Moody
2024-05-12 14:47 ` cinap_lenrek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).