/srv/irclogs.ubuntu.com/2008/10/15/#upstart.txt

Keybuksadmac: I'll digest it over the next day or so, but that looks ... inspired00:40
Keybuksadmac: around?14:35
sadmacKeybuk: ya?14:54
Keybuksadmac: so what I think you're doing here, after a quick glance, is placing a second "iterator head" in the linked list immediately after the node being iterated14:56
sadmacimmediately before14:56
Keybukbefore?14:56
sadmacyes14:56
Keybukah, that explains why it's more complicated than I expected14:56
sadmacwe're always operating on the node after the "cursor" element14:56
sadmachmm, after would make a few things easier....14:57
sadmacI need to get on campus, but I should be online in a couple minutes.15:03
sadmacbrb.15:03
Keybukyeah, just thinking about the difference15:04
KeybukI'd thought about putting the cursor after, since then the node to return is cursor->prev and the next node to visit is cursor->next15:04
Keybukand the worst they can do is remove all of the nodes15:04
Keybukin which case, cursor->next is the list head, which will end the loop15:04
Keybukif they remove the next node, that just means cursor->next is different15:05
Keybukif they remove the visiting node, that just means cursor->prev is different - but we don't even look at that on the loop iteration15:05
Keybukthe bug is caused by "caching" the next node, so the visiting node can be removed15:06
Keybukwhich means if you remove the next node as well, the cache dereferences free'd memory15:07
Keybukby using a cursor element, we only ever cache the cursor, which in theory the code in the loop knows nothing about15:07
Keybuk-- 15:07
Keybukcursor after does mean that if new nodes are inserted after the visiting node, they will not be visited15:07
Keybuknot sure if that's a feature or not15:07
Keybukcursor before means that new nodes inserted after *are* visited15:07
Keybukbut also, strangely, new nodes inserted before might be visited too15:08
Keybukexcept you have a more complicated "step the cursor" case15:08
Keybukand I'm not sure that "step the cursor" is robust if nodes are inserted before the visited node15:08
Keybukin fact, I have a slight feeling that you'll visit the node twice15:08
Keybuk  C-V-X on beginning of loop, becomes15:08
Keybuk  C-A-B-V-X on end of loop15:09
Keybukyou'll see that C-next is not V, so won't step the cursor15:09
Keybukso will visit A, B then V again15:09
Keybukcursor after is safe from that15:09
Keybuk  V-C-X becomes V-A-B-C-X or A-B-V-C-X15:09
Keybukin neither case will A or B be visited in that iteration15:09
sadmachm15:27
sadmacwe could mark the cursor as special, either with a flag or by checking the address, and then modify the insert functions to behave as we'd expect.15:31
sadmacso V-C-X becomes A-B-V-C-X or V-C-A-B-X15:32
KeybukI can't decide whether it's a good thing or not ;P15:32
Keybukif it works with insert() you'd kinda expect it to work with insert_before() too15:32
sadmacthe assumption from the "user" is that the list is being iterated linearly.15:33
sadmacbefore the visited node is not in the damage path15:33
sadmacafter is15:33
Keybukthere's an even more fun way of doing it15:33
Keybukactually, no, that's the same way you're trying to do it, sorry15:34
sadmacheh15:34
KeybukI was thinking of actually removing the visited node, and use the cursor as a list head only15:34
Keybukbut that has exactly the same semantics anyway15:34
sadmachaving nodes inserted after the visited node get iterated and before not is how it would work in the "oracle" case, where we just could divine where we were in the list by magic.15:36
Keybukerr, that wasn't english?15:36
sadmacputting it another way:15:37
sadmacX-A-X-X-X-V-X-X-B15:37
sadmac^if we're at V, and we just inserted A and B, we'd expect A not to get visited and B to get visited15:38
sadmacthat's what regular old NIH_FOREACH would do15:38
Keybukright15:38
sadmacso we should emulate that behavior15:38
sadmacwe want to visit things inserted after, but not things inserted before15:39
Keybukagree15:39
sadmacso the way to do that is if a node is before a cursor and we try to insert_after, to insert after the cursor instead of right after the node15:40
suihkulokkiis there some plan to add dh_installevent style helper 15:41
sadmacsuihkulokki: I have no idea what that is15:41
suihkulokkithat's a debianism15:42
sadmacthat'd do it15:42
Keybuksuihkulokki: no, none15:42
suihkulokkiKeybuk: do you think it would be a good idea?15:42
suihkulokkithis is the third package i'm adding a native upstart event file and I'm noticing a pattern ;)15:43
sadmacsuihkulokki: who knows what the pattern will look like next release though ;)15:44
Keybuksuihkulokki: I do not think dh_ anything is a good idea15:46
Keybukwhat's wrong with just shipping the file as a conffile?15:46
suihkulokkithe point is just to cut down the lines written to each debian/rules file15:49
Keybuksadmac: of course, I forgot that nih_list is kinda backwards15:49
Keybuknih_list_add actually adds before :p15:49
Keybukit's only nih_list_add_after that's special15:49
sadmacKeybuk: right15:49
Keybuksuihkulokki: why do you need any lines in debian/rules?15:50
Keybuksadmac: so you have to try very hard to get the node *after* the one you're looking at15:50
Keybukand because you tried hard, you're likely to expect that it gets iterated15:50
sadmacKeybuk: so in order to dtrt we need to be able to tell cursors from normal elements15:51
sadmacKeybuk: and we can do that one of 2 ways15:51
sadmacKeybuk: 1) the obvious way: add a field to the element with a flag15:51
Keybukexpensive15:51
Keybuk64 bits! for a 1 bit flag15:52
sadmacKeybuk: 2) The evil way: check the address. If its allocated on the stack, its a cursor. if its in heap, its not15:52
Keybuklist elements can be alocated on the stack15:52
sadmacyou're only technically right, I don't think they ever /are/15:52
Keybukyeah they are15:52
sadmacbut, hence the evil15:52
sadmachm15:53
sadmacwe need darker magic15:53
Keybukparse_job.c has one15:53
Keybukparse_job.c:    NihList        stack;15:53
sadmacthat'll do it15:54
sadmacwe could add the flag field, and just come up with lots of ultra cool features so we need more flags :)15:54
sadmacnih_list_set_flag(list, NIH_EAT_BABIES_AND_KITTENS);15:55
Keybukmmm, kittehs15:55
Keybukyour dual for loop really confuses me :p15:59
Keybukthis is you trying to do "before and after" code, right? :)16:00
sadmacyeah16:02
Keybukcouldn't the "test" part of the outer for loop just be 0 ?16:02
Keybukfor (NihList cursor = { &cursor, &cursor }, *_iter = nih_list_add_after ((list), &cursor); FALSE; nih_list_remove (&cursor))16:03
Keybuksomething like that16:03
Keybukie. don't set the pointer to NULL, then you don't need to test it16:03
KeybukI suspect the compiler will optimise that properly anyway, but it makes it a bit more clear16:04
KeybukI don't think you even need _iter in there16:04
Keybukexcept as something to allow you call nih_list_add_after16:04
sadmacno that won't work16:05
sadmacthen the loop will never run16:05
sadmacsetting to null makes it run exactly once16:05
sadmacbecause its not null for the first test16:06
Keybukah, you're quite right16:07
sadmacKeybuk: I'm going to leave it up to you to figure out how to remove the handler function patch from bzr. Its tangled up in quite a few things.16:16
Keybukhandler function patch?16:18
sadmacKeybuk: our previous patchwork fix for a single instance of this error16:21
sadmacwith the marking and sweeping and such16:22
Keybukthat's easy16:22
Keybukbzr diff -r -2..-116:22
Keybukor just bzr diff -r -1..-2 then you don't need -R to patch ;)16:22
sadmacKeybuk: its tangled with some other changes16:23
Keybukah, so it is16:23
sadmacKeybuk: its got the change of the dbus timeout to INT_MAX in there, and it also seems you moved some files16:23
Keybuksomething like bzr diff -r before:594.1.2 I think16:24
sadmactried that but the patch looked weird16:25
sadmacI think you renamed nih_dbus_tool.py to nih_dbus_tool in the same timeframe16:25
Keybukno, the latter is just made from the former16:25
sadmacthe diff I got back was re-adding the INT_MAX change16:25
sadmaclike it wasn't there before16:26
Keybukthat merged from you at the same time I think16:26
sadmachm16:30
Keybuk+                       if (_##iter->next == iter) { \16:31
Keybuk+                       } \16:31
Keybuk+                       iter = _##iter->next; \16:31
Keybuk*cough*16:31
Keybukshouldn't that last "=" be inside the if? :p16:31
Keybukno16:32
Keybuksorry16:32
Keybukconfusing iter and _##iter16:32
Keybukyou don't need the "tmp" inside though ;)16:35
Keybukyou know that _##iter->next and iter are the same16:35
Keybuk+                       if (_##iter->next == iter) { \16:35
Keybuk+                               nih_list_add_after (iter, \16:35
Keybuk+                                       nih_list_remove (_##iter)); \16:35
Keybuk+                       } \16:35
Keybuk+                       iter = _##iter->next; 16:35
Keybukin fact16:37
Keybukyou don't even need it that complicatedf16:37
Keybukif (_##iter->next == iter)16:37
Keybuk        nih_list_add_after (iter, _##iter);16:37
Keybukiter = _##iter->next;16:37
Keybukyeah16:42
Keybukconfirmed16:42
Keybukyour method is unsafe when using nih_list_add ()16:42
Keybukif the caller does nih_list_add () on the entry itself, the cursor will be pushed back in the list16:43
Keybukit correctly won't step16:43
Keybukbut will visit the entry twice16:43
Keybuknot that I can think of any time off-hand where we do that (I tend to call it on the list head), but it's valid - as it's a way of doing sorting and stuff16:43
sadmacok16:57
sadmacso we need to have the cursor after16:57
sadmacand then add_after needs to have something done to it16:57
Keybukcursor afterwards just has opposite problems17:01
sadmachow so?17:01
Keybukit doesn't notice the adds at all :)17:01
KeybukI'm actually wondering if there's an evil way of breaking the pointers :p17:02
sadmacwhich is half-right17:02
sadmacnot noticing the adds is right for add(before) but not for add_after17:02
sadmacthe latter is fixable17:02
sadmachmm..actually you're right. we just need to fix one of the add methods to account for cursors17:03
sadmacthe issue is the cursor getting separated from the iterated node in cases where the iterated node is still in the list17:04
Keybukright, we really want to add them after the cursor17:11
sadmacassuming we switch to cursor after17:14
sadmacwe need to add before the cursor if we keep it as is17:15
Keybukright, which means you need to know where they started adding17:15
Keybukam thinking17:16
Keybukwhat if iter->next is cursor->next17:16
Keybukso the list is slightly broken17:16
Keybuk(iter->next would normally be cursor)17:16
sadmacI'm afraid17:16
Keybukno, there's no safe way to do that17:24
Keybukthe only way is to hide the cursor entirely17:25
Keybukbut then the cursor isn't actually updated17:25
sadmac2there's one 8-byte solution17:36
sadmac2we just need to make it smaller17:36
sadmac2only thing I have so far is put a flag in the MSB of one of the poiters17:39
sadmac2*pointers17:39
Keybukpointers don't have MSBs17:40
sadmac2they can be made to look as though they do.17:40
Keybukno, they can't17:43
Keybukyou can't fiddle with a pointer, it's in scary undefined territory according to the spec17:43
sadmac2yeah yeah17:44
sadmac2this is what I get for reading kernel code17:44
sadmac2now to class17:46
=== Md_ is now known as Md
Keybuk#define NIH_LIST_FOREACH_SAFE(list, iter)                               \19:14
Keybuk        for (NihList _##iter = { &_##iter, &_##iter },                  \19:14
Keybuk                 *iter = nih_list_add_after ((list)->next, &_##iter)->prev; \19:14
Keybuk             iter != (list) && iter != &_##iter;                        \19:14
Keybuk             iter = nih_list_add_after (_##iter.next, &_##iter)->prev)19:14
Keybukno, that doesn't quite work, since it leaves the cursor in the list19:19
sadmac2Keybuk: that's what the outer for was for19:40
Keybuk#define NIH_LIST_FOREACH_SAFE(list, iter)                               \20:45
Keybuk        for (NihList _##iter##_cursor = { &_##iter, &_##iter },         \20:45
Keybuk                     _##iter = &_##iter##_cursor;                       \20:45
Keybuk             _##iter;                                                   \20:45
Keybuk             nih_list_destroy (_##iter), _##iter = NULL)                \20:45
Keybuk                for (NihList *iter = nih_list_add_after ((list)->next, _##iter)->prev; \20:45
Keybuk                     iter != (list) && iter != _##iter;         \20:45
Keybuk                     iter = nih_list_add_after (_##iter->next, _##iter)->prev)20:45
Keybuk#20:45
sadmac2Keybuk: is it ok to call nih_list_destroy on something stack-allocated?20:47
sadmac2Keybuk: and either way, why isn't it nih_list_remove?20:47
Keybukyeah, it just cuts it out of he list20:47
Keybukdestroy is cheaper than remove :p20:47
sadmac2ah20:47
Keybukdestroy => nih_list_cut ()20:47
Keybukremove => nih_list_cut (), nih_list_init ()20:47
sadmac2yeah20:48
sadmac2Keybuk: don't you need a * on line 2 before _##iter ?20:51
Keybukyes20:53
sadmac2Keybuk: it looks good. It has the issue of missing insertions that we expected21:00
sadmac2Keybuk: so its just down to that21:00
sadmac2adding the flag costs us 64 bits (128 if you allocated it with a free-list-based malloc)21:03
sadmac2Keybuk: could we use a char for the flag, and then add the gcc flag for struct packing to get rid of the overallocation?21:04
sadmac2damn, but putting it in an unpacked struct would still round it up again21:04
Keybukindeed, very expensive for a single bit flag21:14
sadmac2Keybuk: we could keep a global list of cursors21:19
Keybukthat's 64-128 bits each21:20
sadmac2Keybuk: but only for the cursors21:20
sadmac2Keybuk: not for /every/ list node21:21
Keybuktrue21:21
Keybukyou need to check for cursorness on every insertion though21:21
sadmac2Keybuk: only on insert_after21:23
sadmac2Keybuk: which as you said is the far rarer case21:24
Keybukno, both21:24
Keybuknih_list_add (iter->next, entry)21:24
Keybukyou need to check whether iter->next is a cursor21:24
sadmac2yeah21:25
sadmac2the check is one comparison21:25
sadmac2not a terrible expense21:25
sadmac2we get a jump when it /is/ a cursor21:25
sadmac2but other than that its 2 instructions21:25
sadmac2oh, wait21:25
sadmac2we're still on the list of cursors idea21:26
* sadmac2 is on to his next, more evil thought21:26
sadmac2what if we added the flag field, but only when it was a cursor21:26
sadmac2?21:26
sadmac2so when we call nih_list_new, which means we are getting a struct which is not attached to an object, we over-allocate by 64 bits21:28
sadmac2and write 0xdeadbeafcafebabe /after/ the struct21:28
Keybukunreliable21:29
sadmac2where's the hole?21:29
Keybukyou don't know that all structs in the list are that long21:29
Keybukand you don't know the user isn't using that exact string :p21:29
Keybuke.g. the list head is smaller, you'd read unallocated memory21:29
sadmac2user using the exact string is one in several billion billion21:29
sadmac2struct not that long isn't possible due to struct member allignment21:29
sadmac2(well, 32-bit folk get jacked a bit)21:30
sadmac2we have a different value for cursors, and regular stack-allocated lists have stack space in front of them supplying the bullshit21:31
Keybukno, it's entirely possible21:31
KeybukI don't like even remote chances21:32
Keybukit must only succeed _if_ it's a cursor21:32
Keybuknot just something that looks like one21:32
sadmac2better odds? make the value a hash of the prev and next pointers21:32
Keybukno21:33
sadmac2you wouldn't do well in cryptography :)21:35
Keybukit must only succeed if it is a cursor, not if it looks like one21:35
Keybukand, pointedly, it mustn't crash on the list head <g>21:35
sadmac2we could set aside some memory at startup, and allocate all cursors in it21:36
sadmac2then we could check address range21:36
Keybukwhat if you run out of that memory?21:44
sadmac2get a bigger chunk and rename21:44
sadmac2Keybuk: the check if on stack method works if we just enforce via documentation21:50
sadmac2Keybuk: are you sure the example you found was on the stack, not a global?21:50
Keybukdefinitely on the stack22:42

Generated by irclog2html.py 2.7 by Marius Gedminas - find it at mg.pov.lt!