February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On Thursday, 6 February 2014 at 19:08:40 UTC, Adam D. Ruppe wrote:
> On Thursday, 6 February 2014 at 18:52:21 UTC, fra wrote:
>> Hey, wait a second. How do you throw without allocating?
>
> I think exceptions should be ok. You optimize the typical path, and exceptions are (by definition) an exceptional path. If they are also unacceptable, you could restrict yourself to nothrow functions. (Which can still throw Errors... but meh they are even *more* exceptional)
Hardly so. Any exception allocation can trigger GC collection cycle and Phobos does not provide any other way to handle data errors. Any application that operates on some external user input will be subject to DoS attack vector if it uses Phobos directly.
It was huge performance killer for vibe.d last time I have checked, for example.
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On Thursday, 6 February 2014 at 19:54:27 UTC, Sean Kelly wrote:
> Does this case even matter? Exceptions are not a normal function of execution, and so should happen rarely to never. And it's a time when I'd expect a delay anyway.
Imagine intentionally crafted broken utf as user input in repeated requests. You don't have control over it.
Now if Phobos would have only thrown exceptions in really _exceptional_ situations and handled broken input gracefully...
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 6 February 2014 at 21:38:03 UTC, Dicebot wrote:
> On Thursday, 6 February 2014 at 19:08:40 UTC, Adam D. Ruppe wrote:
>> On Thursday, 6 February 2014 at 18:52:21 UTC, fra wrote:
>>> Hey, wait a second. How do you throw without allocating?
>>
>> I think exceptions should be ok. You optimize the typical path, and exceptions are (by definition) an exceptional path. If they are also unacceptable, you could restrict yourself to nothrow functions. (Which can still throw Errors... but meh they are even *more* exceptional)
>
> Hardly so. Any exception allocation can trigger GC collection cycle and Phobos does not provide any other way to handle data errors. Any application that operates on some external user input will be subject to DoS attack vector if it uses Phobos directly.
>
> It was huge performance killer for vibe.d last time I have checked, for example.
Personally I don't think bad user input qualifies as an exceptional case because it's expected to happen and the program is expected to handle it (and let the user know) when it does. That's just a matter of taste though.
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 6 February 2014 at 21:48:13 UTC, Dicebot wrote: > On Thursday, 6 February 2014 at 19:54:27 UTC, Sean Kelly wrote: >> Does this case even matter? Exceptions are not a normal function of execution, and so should happen rarely to never. And it's a time when I'd expect a delay anyway. > > Imagine intentionally crafted broken utf as user input in repeated requests. You don't have control over it. > > Now if Phobos would have only thrown exceptions in really _exceptional_ situations and handled broken input gracefully... You should probably validate utf from all foreign sources. Catch a problem with it as it comes in rather than in some arbitrary part of your program. http://dlang.org/phobos/std_utf.html#.validate |
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Anderson | On Thursday, 6 February 2014 at 22:15:11 UTC, Brad Anderson wrote:
> Personally I don't think bad user input qualifies as an exceptional case because it's expected to happen and the program is expected to handle it (and let the user know) when it does. That's just a matter of taste though.
I agree. It kills the whole concept of "exceptions are rare so they don't need to be fast when thrown". But it is how quite lot of Phobos is currently designed and, in my opinion, is biggest design mistake of vibe.d too (it uses exceptions to propagate HTTP status codes)
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Anderson | On Thursday, 6 February 2014 at 22:18:10 UTC, Brad Anderson wrote:
> You should probably validate utf from all foreign sources. Catch a problem with it as it comes in rather than in some arbitrary part of your program.
>
> http://dlang.org/phobos/std_utf.html#.validate
pure @safe void validate(S)(in S str) if (isSomeString!S);
Throws:
UTFException if str is not well-formed.
;)
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 6 February 2014 at 22:19:42 UTC, Dicebot wrote:
> On Thursday, 6 February 2014 at 22:15:11 UTC, Brad Anderson wrote:
>> Personally I don't think bad user input qualifies as an exceptional case because it's expected to happen and the program is expected to handle it (and let the user know) when it does. That's just a matter of taste though.
>
> I agree. It kills the whole concept of "exceptions are rare so they don't need to be fast when thrown". But it is how quite lot of Phobos is currently designed and, in my opinion, is biggest design mistake of vibe.d too (it uses exceptions to propagate HTTP status codes)
I must admit that I am guilty of sometimes using exceptions for routine control flow too. It's just so convenient compared to validation/consumption.
Maybe we should make a list of Phobos functions that throw exceptions and ensure that (for the ones where this makes sense) they non-throwing validators available. If we can stop gc allocating them that'd be even better but I don't think them being gc allocating should hold up @nogc.
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 6 February 2014 at 22:20:38 UTC, Dicebot wrote:
> On Thursday, 6 February 2014 at 22:18:10 UTC, Brad Anderson wrote:
>> You should probably validate utf from all foreign sources. Catch a problem with it as it comes in rather than in some arbitrary part of your program.
>>
>> http://dlang.org/phobos/std_utf.html#.validate
>
> pure @safe void validate(S)(in S str) if (isSomeString!S);
>
> Throws:
> UTFException if str is not well-formed.
>
> ;)
Heh, well then... let me just wipe this egg off my face. :P
|
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Thursday, 6 February 2014 at 21:38:03 UTC, Dicebot wrote: > Any application that operates on some external user input will be subject to DoS attack vector if it uses Phobos directly. Hmm, I hadn't considered that. Maybe exceptions could be handled automatically though due to the facts that there are rarely more than one in flight at any time and they typically don't live for long: 1) prohibit escaping of exception objects from catch blocks (we could just say it is undefined behavior in the spec). The data pointed to by the throwable object should be normal though, if you want to keep the exception, you can thus just shallow copy it. 2) Set aside a static (thread local) buffer early on with a size of like 512 bytes. 3) Make "throw new" call a special function which favors the static buffer. It can do a simple bump-the-pointer allocation in the static region or call the regular GC if there isn't enough room (should be extremely rare). throw e; works the same way it does now. You can pre-allocate with some other method if you want. 4) Have the compiler automatically insert a call to _d_free_exception in a scope(success) block inside every catch block. It checks the given reference, if it is in the static buffer, just zero it all out. If all the chain is in there, zeroing it will free it all. If there's any GC chained exceptions, zeroing it will orphan them and they'll be freed on the next sweep. Otherwise ... well do nothing, let the GC clean up after it. Proof of concept: bool isThrowable(const ClassInfo ci) { if(ci is null) return false; if(ci is typeid(Throwable)) return true; return isThrowable(ci.base); } byte[512] exceptionHolder = 0; size_t exceptionHolderPosition = 0; extern(C) Object _d_newclass(const ClassInfo ci) { if(!isThrowable(ci)) return _d_newclass_original(ci); auto size = ci.init.length; if(exceptionHolderPosition + size > exceptionHolder.length) return _d_newclass_original(ci); byte[] slice = exceptionHolder[exceptionHolderPosition .. exceptionHolderPosition + size]; exceptionHolderPosition += size; slice[] = ci.init[]; import core.stdc.stdio; printf("Magic allocation to %d\n", exceptionHolderPosition); return cast(Object) slice.ptr; } extern(C) void _d_freeexception(Throwable t) { auto ptr = cast(void*) t; if(ptr >= exceptionHolder.ptr && ptr < exceptionHolder.ptr + exceptionHolder.length) { exceptionHolder[] = 0; exceptionHolderPosition = 0; import core.stdc.stdio; printf("Freeing\n"); } // else do nothing, the GC will handle it } void main() { import std.stdio; try { writefln("%s"); // orphaned argument } catch(Exception e) { scope(success) _d_freeexception(e); writeln(e); } } // copy/paste from druntime as fallback extern (C) void onOutOfMemoryError(); extern (C) void* gc_malloc( size_t sz, uint ba = 0 ); extern (C) Object _d_newclass_original(const ClassInfo ci) { import core.stdc.stdlib; static import core.memory; alias BlkAttr = core.memory.GC.BlkAttr; void* p; if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass) { p = malloc(ci.init.length); if (!p) onOutOfMemoryError(); } else { // TODO: should this be + 1 to avoid having pointers to the next block? BlkAttr attr = BlkAttr.FINALIZE; // extern(C++) classes don't have a classinfo pointer in their vtable so the GC can't finalize them if (ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass) attr &= ~BlkAttr.FINALIZE; if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers) attr |= BlkAttr.NO_SCAN; p = gc_malloc(ci.init.length, attr); } // initialize it (cast(byte*) p)[0 .. ci.init.length] = ci.init[]; debug(PRINTF) printf("initialization done\n"); return cast(Object) p; } === Just compile and run normally, the linker will prefer our d_newclass to the one in phobos.lib automatically. And you'll see the throw from writeln went into our static buffer and was freed at the end. I toyed with a few other things too: void main() { import std.stdio; try { try { writefln("%s"); // orphaned argument } catch(Exception e) { scope(success) _d_freeexception(e); // don't forget these throw new Exception("LOL", e); } } catch(Exception e) { scope(success) _d_freeexception(e); writeln(e); writeln(e.next); } } still works. Am I missing a fatal flaw here? It seems to work and is kinda simple to do... exceptions don't really need a huge amount of dynamic memory. |
February 06, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On Thursday, 6 February 2014 at 22:56:45 UTC, Adam D. Ruppe wrote: > Proof of concept: code in a link so the lines aren't broken http://arsdnet.net/dcode/except.d |
Copyright © 1999-2021 by the D Language Foundation