Defer icon indicating copy to clipboard operation
Defer copied to clipboard

Make it more portable?

Open nicowilliams opened this issue 4 years ago • 12 comments

goto *_deferrals[--_num_deferrals] is not portable C :(

There is good news though: you could use a Duff's device so that each deferral is a switch case and then you can while (_num_deferrals) loop around the switch. Not unlike async.h or Simon Tatham's C co-routines.

nicowilliams avatar Mar 18 '20 22:03 nicowilliams

There is a portable version using setjmp/longjmp (look at the second half of the file); unfortunately, because longjmp tries to be clever, it causes problems when optimizations are enabled, and with dynamic stack allocations.

I did attempt a switch-based version, but was unsuccessful.

Leaving this open as a tracking issue for portability, though.

moon-chilled avatar Mar 18 '20 22:03 moon-chilled

The other problem with a switch/case-based version is that you need to have another macro to put a closing brace at the end of the function. Which is not a huge deal, but it still adds friction and makes the library less 'natural' to use.

moon-chilled avatar Mar 18 '20 22:03 moon-chilled

Yeah, I thought of the closing brace, but I'm ok with that. I can't use this with the setjmp()/longjmp() approach, and I can't use GCC/clang extensions. I'm also not sure I'd use it if it used a Duff's device either, but I am sure I need this :)

nicowilliams avatar Mar 19 '20 00:03 nicowilliams

Oh, I think I see the problem with a Duff's device...

EDIT: Nah, made it work! PR submitted! Feel free to reject, naturally. And, yes, sadly, I did need BeginDeferral/EndDeferral macros :(

nicowilliams avatar Mar 19 '20 00:03 nicowilliams

Can you elaborate on why you can't use the gcc extensions? Using MSVC? Or on an embedded platform?

This can be implemented safely using relatively trivial assembly routines; if that would suffice, let me know the platform and I can whip something up.

moon-chilled avatar Mar 19 '20 01:03 moon-chilled

Yeah, MSVC :(

nicowilliams avatar Mar 19 '20 02:03 nicowilliams

So if I make a version which uses amd64 assembly (masm syntax, I guess?), would that work for you?

moon-chilled avatar Mar 19 '20 04:03 moon-chilled

Don't waste your time on that yet. If I could use this, I'd use it in Heimdal), but that would require consensus among us maintainers. I did the Duff's device thing for the fun of it. Thanks!

nicowilliams avatar Mar 19 '20 15:03 nicowilliams

I've figured out how to make it work with MSVC using ASM. Essentially we need two macros, TakeLabelAddress(destination, label_name) and GotoLabelAddress(addr) that look like this:

#define TakeLabelAddress(dest, l) \
    do { void *_label; __asm{ mov [_label],offset l }; dest = _label; } while(0)
	
#define GotoLabelAddress(a) do { void *_label = (a); __asm{ jmp _label } } while (0)

A simplified defer.h for just MSVC (i.e., w/o #ifdef spaghetti) looks like this:

#ifndef DEFER_H
#define DEFER_H

#ifndef DEFER_MAX_DEFERRED_STATEMENTS
# define DEFER_MAX_DEFERRED_STATEMENTS 32
#endif

#define TakeLabelAddress(dest, l) \
    do { void *_labelv; __asm{ mov [_labelv],offset l }; dest = _labelv; } while(0)
	
#define GotoLabelAddress(a) do { _label = (a); __asm{ jmp _label } } while (0)


#define Deferral \
unsigned char _num_deferrals = 0; \
void *_defer_return_loc = 0, *_deferrals[DEFER_MAX_DEFERRED_STATEMENTS] = {0};

# define Defer(block) _Defer(block, __COUNTER__)
# define Return _Return(__COUNTER__)

#define _defer_tokpaste(a, b) a ## b

#define _Defer(block, n) do { \
	TakeLabelAddress(_deferrals[_num_deferrals++], _defer_tokpaste(_defer_ini, n)); \
	if (0) { \
		_defer_tokpaste(_defer_ini, n): \
		block; \
		if (_num_deferrals) { \
			GotoLabelAddress(_deferrals[--_num_deferrals]); \
		} else { \
			GotoLabelAddress(_defer_return_loc); \
		} \
	} \
} while (0)

#define _Return(n) \
	if (_num_deferrals) { \
		TakeLabelAddress(_defer_return_loc, _defer_tokpaste(_defer_fini_, n)); \
		GotoLabelAddress(_deferrals[--_num_deferrals]); \
	} \
\
	_defer_tokpaste(_defer_fini_, n): \
	return


#endif /*DEFER_H*/

nicowilliams avatar Mar 20 '20 22:03 nicowilliams

Yes, I actually made pretty much that, when I was testing initial versions. But I canned it because __asm only works with 32-bit msvc, and I don't really care about 32-bit x86.

moon-chilled avatar Mar 20 '20 22:03 moon-chilled

It doesn't work for 64-bit??

nicowilliams avatar Mar 20 '20 22:03 nicowilliams

Ugh, you're right.

nicowilliams avatar Mar 20 '20 22:03 nicowilliams