stuck in getUniqueSerial

Find any bugs in Vega Strike? See if someone has already found it, or report them here!
Post Reply
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

stuck in getUniqueSerial

Post by peter »

VS is currently stuck in getUniqueSerial, during a jump to Proxima Centauri. There are a lot of flightgroups in the area, so maybe VS ran out of unique serial numbers. MAXSERIAL is 0xffff, which isn't all that high...

Code: Select all

(gdb) bt
#0  getUniqueSerial () at src/vsfilesystem.cpp:65
#1  0x0000000000806615 in Mission::call_unit_launch (this=<value optimized out>, fg=0x7fff08f7ba10, type=0, 
    destinations=@0x7fff08f7bad0) at src/cmd/script/script_call_unit_generic.cpp:1243
#2  0x00000000007b5431 in UniverseUtil::launchJumppoint (name_string=


58      ObjSerial       getUniqueSerial()
59      {
60              int offset = (SERVER ? 2 : 1);
61
62              std::map<ObjSerial,bool>::const_iterator iter;
63              ObjSerial ret;
64              do {
65                      serial_seed = (serial_seed+3)%MAXSERIAL;
66                      ret = serial_seed+offset;
67              } while ((iter=usedSerials.find(ret))!=usedSerials.end()
68                               && (*iter).second);
Any time I break, it's somewhere in this do-loop. gdb isn't doing a great job, because the binary was built with -O3, so values are in registers where gdb doesn't print them as the variable's value.. e.g. serial_seed = 0 always, according to gdb. :/

(I'm using a binary compiled from SVN with

Code: Select all

./configure --enable-flags='-g -march=native -O3' --enable-stencil-buffer
. So not with -ffast-math, and with debugging symbols. I was hoping to fly around for a while before heading back to Luna to see if they have any clydesdales in stock. Other than that, I don't have anything major un-saved, so it wouldn't be a big loss to restart the engine. I don't see any obvious way to get out of the infinite loop without corrupting the game's data structures with duplicate serials. I don't know what VS uses them for...

I thought I'd post here before putting this on the bug tracker, in case it's been seen before. I'll wait for a while for a response before doing anything with the game. It's stopped by gdb now, so I can gather any info you want. I know c++, so don't be afraid to ask for info that requires some exploring on my part. (Although I have no idea what. I'm assuming that VS just has too many flightgroups or units in existance, and that the only way around this would be to delete old ones or limit creation of new ones. Or increase the limit.)
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
RedAdder
Bounty Hunter
Bounty Hunter
Posts: 149
Joined: Sat Jan 03, 2009 8:11 pm
Location: Germany, Munich
Contact:

Re: stuck in getUniqueSerial

Post by RedAdder »

IMHO the only useful thing you could do would be to lookup the size of std::map<ObjSerial,bool> usedSerials; to confirm that all serials are being used. Not sure if you can do this with gdb? Actually it appears even that doesn't help, as the value of items in usedSerials might be "false".

Also, the comment about serials not intersecting with server serials is interesting because instead of setting MAXSERIALS to 0xffff the serials could have been used more efficently by setting it to an even number and using +2 instead of +3. Then you would have to add special code to avoid an id of zero though.
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

RedAdder wrote:IMHO the only useful thing you could do would be to lookup the size of std::map<ObjSerial,bool> usedSerials; to confirm that all serials are being used. Not sure if you can do this with gdb?
It's 21845 = 65535/3, so yeah, probably full. The fact that the loop never exits seems to confirm that none of the serials it's checking are false. Since I'm playing single player, I'll try generating serials that are supposed to be reserved for the server.

Code: Select all

 
(gdb) p usedSerials
$3 = {_M_t = {
    _M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<const short unsigned int, bool> > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<const short unsigned int, bool> > >> = {<No data fields>}, <No data fields>},
      _M_key_compare = {<std::binary_function<short unsigned int, short unsigned int, bool>> = {<No data fields>}, <No data fields>}, _M_header = {_M_color = std::_S_red, _M_parent = 0x2f7ccc30, _M_left = 0xe8d4220,
        _M_right = 0x7ff8c4868350}, _M_node_count = 21845}}}

(gdb) p usedSerials.size()
Cannot evaluate function -- may be inlined
You're right, it's not straightforward, but luckily it's possible because of the simple implementation:
size() const { return _M_impl._M_node_count; }

Actually it appears even that doesn't help, as the value of items in usedSerials might be "false".

Also, the comment about serials not intersecting with server serials is interesting because instead of setting MAXSERIALS to 0xffff the serials could have been used more efficently by setting it to an even number and using +2 instead of +3. Then you would have to add special code to avoid an id of zero though.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

Why is there a std::map for serial allocation anyway? Why not a 64kbit allocation bitmap? It would be much faster and more memory-efficient to search linearly for the next available serial than STL's rb-tree map. (AFAICT, it is using a red-black tree for std::map). At least serial_seed is static (err, global, actually), so it doesn't start from the beginning every time.

Ok, first of all SERVER should be a preprocessor define, so the compiler knows the value at compile time. Currently it's a global defined to one of 3 values in file that have their own main() function: main.cpp (SERVER = 0), vegaserver.cpp (SERVER=1), and accountserver.cpp (SERVER=2). That's just silly, because the compile has to include code for both sides of the conditional into every binary.

So any single binary only needs to worry about allocating either server or non-server serials, so it only needs an allocation bitmap for that range. And as RedAdder says, we might as well use half the space, not 1/3rd.

So, a single 32kb (4kB) bitmap, and we multiply the position by 2 and add an offset of 1 or 2 (so we never generate serial = 0). This way, a full bitmap is all 0xff bytes, so searching for a free serial is a matter of looking for a byte that's not 0xff, and then finding the free bit position.

Ok, and another thing, serials never seem to be freed. So obviously you're going to run out. In this game that froze, VS had been running for a couple days (I just left it running when I stopped, instead of restarting). So obviously you're going to run out of serials at some point if you never free them!!

I was going to start working on a bitmap implementation of getUniqueSerial, and I went looking for where they're freed again. Nothing calls usedSerial(serial, false). And ObjSerial is just a typedef for unsigned short (networking/const.h), so it's not an object itself that could have a method to mark itself as unused.

So it seems to me that the real problem is that VS leaks serial numbers by design, and writing a faster serial allocator that makes better use of the allowed range is a waste of time until we think of a way to avoid having VS freeze after running for a while. (Although it's not like it's hard to mark a serial as unused again in an allocation bitmap. Otherwise I think allocation bitmaps wouldn't be so popular in memory management and filesystem code.)
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

I wrote a new serial number allocator that isn't ridiculously inefficient. Although I'm sure the old one wasn't a perf bottlenck. I like tilting at windmills.

More importantly, it returns 0 when there aren't any more unique serial numbers, for lack of anything better to do. (It never returns 0 under any other conditions.) Instead of the inefficient interleave of the old one, serials with the high bit set are server serials. The lower half of the range are client serials. (except I return 0 as an error even when SERVER != 0).

I've attached genserial.cpp. There's a main() function, so it compiles to a stand-alone test of the allocator. It should work fine with 32 or 64bit unsigned longs, big or little endian, since I use sizeof(unsigned long) instead of putting in any constants. It should actually work fine with any word size. The only assumption is that bytes have at least 8 bits. If there are more, they'll be wasted. Although VS probably won't run on anything old enough to have odd byte/word sizes like that :wink:.

I looked at Linux's bitmap functions for inspiration, and was lucky to find the ffsl() function mentioned there. That saves coding up an ugly shift loop to find the first set bit in a word, which some CPUs can do with a machine instruction, but C doesn't have an operator for. Well, it does now (if you can depend on POSIX) , because ffs(int) is in POSIX.1-2001. ffsl(long) is only a GNU extension, though. stupid POSIX :roll:.

I haven't tried putting this into Vegastrike myself, yet. I thought I post here first.
You do not have the required permissions to view the files attached to this post.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

I just realized why SERVER is not a compile-time constant. So you don't have to recompile every file that has client/server conditionals when building vegastrike and then the server.

I replaced getUniqueSerial with my re-implementation, and VS runs ok with it. Instead of doing an odd/even thing, I set the high bit for server serials, and clear it for client serials. So client serials are 1-32767, server has 32768-65535. My game is running ok so far. I'll post a patch soon.

I had to remove the setting of serial_seed in networking/acctserver.cpp and networking/netserver.cpp, since I only keep track of what unsigned long the last free bit was found in, not the exact number. Serials don't need to be random, only unique, right? If this depends against some sort of multiplayer attack, then the old algorithm of += 3 unless already used is not exactly strong regardless of starting at a random point. I could have my version start at a random multiple of 32 or 64, though (depending on sizeof(unsigned long)).

BTW, is GNU ffsl(long) available on whatever VS is compiled with on Windows? How about POSIX ffs(int)? Because I could easily just use unsigned ints instead of longs, since this isn't used anywhere performace-critical anyway. But I do want to keep ffs of some form, because gcc compiles it to the bsf instruction (bit-scan-forward), and the perfectionist in me would _hate_ to see code that compiled to a stupid shift-and-test loop. It might be a gcc built-in function, but it might just be inline asm in libc.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

here's an SVN diff. It's maybe excessively commented, so feel free to remove some before comitting. (err, if you want to commit it at all :mrgreen:) I didn't put my name in a copyright anywhere, but obviously I'm putting this code under the GPL. share and enjoy.

The most important change from the old code is that now getUniqueSerial prints a warning and returns 0 when there aren't any unused serials left. This seems like a better idea than an infinite loop. I've provided a freeSerial function that clears the corresponding bit in the bitmap back to 0. I haven't put calls to it anywhere, since I haven't dug into VS's design, so I don't know where it stops using numbers.

BTW, this compiles and plays fine (in single-player mode) on my AMD64 Ubuntu Intrepid system. I did test the allocator code on my 32bit x86 laptop, so it appears to be 64bit clean, as I intended.
You do not have the required permissions to view the files attached to this post.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
RedAdder
Bounty Hunter
Bounty Hunter
Posts: 149
Joined: Sat Jan 03, 2009 8:11 pm
Location: Germany, Munich
Contact:

Re: stuck in getUniqueSerial

Post by RedAdder »

You need to assert your copyright on the code to make it licensed under the GPL.

The GNU GPL isn't only about simply giving the code away, it reserves you the right to sue in court any bastard who makes use of it outside the GNU GPL. You might even end up making money, since such a bastard might be forced to buy a license from you.

Of course that is taking the thoughts a bit too far when all you want to do is donating a little function.

See also: http://www.gnu.org/licenses/gpl-howto.html

Regarding the functions, I don't want to leave the impression that I read them all, but i have a question regarding

Code: Select all

void freeSerial(ObjSerial s)
{
	if (SERVER) s &= ~( (MAXSERIAL+1)/2 );  // discard the high bit
	int pos   = s / (8*sizeof(unsigned long));
	int shift = s % (8*sizeof(unsigned long));
	usedSerials[pos] &= ~(1UL << shift);
}
Actually my first question disappeared, but I have a second question, which also disappeared half-way:

How can you be sure that MAXSERIAL+1 doesn't evaluate to zero?
Are macro'ed constants always evaluated as long?
See http://home.att.net/~jackklein/c/inttypes.html "int must have at least 16 bits"

I see that it works in my test case(g++.exe (GCC) 3.4.2 (mingw-special)) though:

Code: Select all

#include "stdio.h"
int main(){
	printf("%d",(int)((0xFFFF+1)/2));
	return 0;
}
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

RedAdder wrote:You need to assert your copyright on the code to make it licensed under the GPL.
...
Of course that is taking the thoughts a bit too far when all you want to do is donating a little function.
Yes, exactly. I can also just give it away (public domain), and then the VS devs are free to include it in the GPLed vegastrike. Which is ok by me. I'm a fan of the GPL, and the GNU philosophy... :)

How can you be sure that MAXSERIAL+1 doesn't evaluate to zero?
Are macro'ed constants always evaluated as long?
See http://home.att.net/~jackklein/c/inttypes.html "int must have at least 16 bits"

I see that it works in my test case(g++.exe (GCC) 3.4.2 (mingw-special)) though:

Code: Select all

#include "stdio.h"
int main(){
	printf("%d",(int)((0xFFFF+1)/2));
	return 0;
}
You're right that if int was only 16bits, I think my code would fail. Maybe MAXSERIAL/2 + 1 would be a better way to write it, on the assumption that MAXSERIAL will always be 2^n - 1, so you'll get the same result either way.

Macros don't define constants, they define text substitutions. C (which I know better than C++) has specific rules about types for constants. IIRC, 0xFFFF is an int, so (0xFFFF+1)/2 will be evaluated as an int, and it doesn't overflow because int is at least 32bits on anything VS will run on. It's only after it is evaluated to 0x8000 that &ed with an ObjSerial (a typedef for unsigned short int, 16 bits). Note the use of 1UL, an unsigned long constant, to avoid any possibility of the left shift pushing it off the end of an int before it is cast to unsigned long, for LP64 machines (long and pointer 64bits, int 32bits).

I was curious so I looked it up (for C: K&R, appendix A2.5.1). integer constants are interpreted as at least int, and as a wider type if they can't be represented as int. For hex constants, it's int, unsigned int, long int, unsigned long int. For decimal constants, unsigned int isn't an option. There is no type qualifier for short int, only U/u and L/l for unsigned and long.

According to C's type promotion rules, when I do unsigned short &= int, integral promotion happens to both sides, which will promote the unsigned short to int, because int can represent all its possible values. Then the & happens. Then the result is truncated down to unsigned short to be stored in an ObjSerial variable. So the constant expression is evaluated as an int, not a short int.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
RedAdder
Bounty Hunter
Bounty Hunter
Posts: 149
Joined: Sat Jan 03, 2009 8:11 pm
Location: Germany, Munich
Contact:

Re: stuck in getUniqueSerial

Post by RedAdder »

Maybe MAXSERIAL/2 + 1 would be a better way to write it, on the assumption that MAXSERIAL will always be 2^n - 1, so you'll get the same result either way.
Yes, I think that might be better, because you are already assuming it in (0xFFFF+1)/2.
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

RedAdder wrote:
Maybe MAXSERIAL/2 + 1 would be a better way to write it, on the assumption that MAXSERIAL will always be 2^n - 1, so you'll get the same result either way.
Yes, I think that might be better, because you are already assuming it in (0xFFFF+1)/2.

Well, currently only in how I use the result (bit masking with it, instead of adding and subtracting it). But yeah, I'll do that, then, so it won't break if we decide we want 32bit serials.

I'm still waiting for enlightenment on why they are never actually freed, and what the design is supposed to be.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

Ok, with some more tweaking, I'm now happy with my code. I made some changes based on looking at the asm that g++ produced, because I'm obsessive like that. Now the function is a few insns smaller 8). VS still runs normally with it, and I tested it with my main() function to make sure it still does what it's supposed to.

More importantly, I revised how I write the constants, to avoid integer overflow at compile time if MAXSERIAL = 2^32 - 1 (or 2^64 - 1).

And I improved the comments. Note that I found a plain-C implementation of ffs() in the Linux kernel sources, in case you need to include one for VS to compile on platforms that don't have GNU's ffsl(). See linux/include/asm-generic/bitops/ffs.h
You do not have the required permissions to view the files attached to this post.
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

bump. Is anyone looking at this code, or what?
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
ace123
Lead Network Developer
Lead Network Developer
Posts: 2560
Joined: Sun Jan 12, 2003 9:13 am
Location: Palo Alto CA
Contact:

Re: stuck in getUniqueSerial

Post by ace123 »

I doubt this was responsible for many crashes, but it definitely needs fixing regardless.

Sorry, I downloaded this patch to my desktop but never got around to applying it.
I'll commit it to SVN tonight.

I don't think the MAXSERIAL is going to need changing any time soon--since neither the server nor the clients can support anywhere near 30 thousand units at once.
But then again look at what Bill Gates said about only needing 640k of memory, not to mention the fact that his latest Windows monstrosity needs 1000 times as much just to run.
ace123
Lead Network Developer
Lead Network Developer
Posts: 2560
Joined: Sun Jan 12, 2003 9:13 am
Location: Palo Alto CA
Contact:

Re: stuck in getUniqueSerial

Post by ace123 »

I like most of it--specifically that you used an array. (though we may want to rethink that if we get more than 64k serials--I wouldn't want a 512MB array for serial numbers--switching to a 64-bit integer and assuming that they never wrap would be easier.

But I don't like that you rely on that non-constant initializer--who knows if that works in GCC3 or 2.95, or Visual C++, exactly how you want. However, I do see what you mean about not wanting this to be a lot of 0's in the executable file. I'm going to make a simple class whose constructor initializes all the elements of the array to make it more compatible unless you have another idea.
Then that class could also take care of bitmasking in its inline [] operator...
speaking of which, doesn't vector<bool> take care of this? I thought that was required to be implemented as a bitmap...
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

ace123 wrote:Sorry, I downloaded this patch to my desktop but never got around to applying it.
I'll commit it to SVN tonight
Great. :D Did you want me to make the changes discussed below (implementing it as a class, and using Boost's dynamic_bitset class), or do you already have it half done?
ace123 wrote:I like most of it--specifically that you used an array. (though we may want to rethink that if we get more than 64k serials--I wouldn't want a 512MB array for serial numbers--switching to a 64-bit integer and assuming that they never wrap would be easier.
Yeah, good thought. Even if you never free serial numbers (as is currently the case, so tracking their allocation is useless), you're unlikely to run out with 64bit. Note that in VS currently, nothing ever frees a serial number. I originally ran into problems (VS got stuck in an infinite loop) when it ran out of serial numbers after running for a long time and making several jumps between systems with lots of ships (in and around Sol). So this is currently a major problem, and nothing in my patch fixes it. I do provide a function that _can_ be called, but as I said, I didn't know where to put any calls to it, because there aren't any calls to usedSerial(n, false) anywhere :!: :evil: A bunch of new serial numbers are allocated when you jump, even to a system you've been to recently. AFAICT, one per ship in the system.
But I don't like that you rely on that non-constant initializer--who knows if that works in GCC3 or 2.95, or Visual C++, exactly how you want. However, I do see what you mean about not wanting this to be a lot of 0's in the executable file. I'm going to make a simple class whose constructor initializes all the elements of the array to make it more compatible unless you have another idea.
The non-constant initializer was sort of a hack job, yeah. The've been supported since before gcc3 IIRC, but the best thing would be to put a line of code somewhere to do the initialization in some other init function in the same file. (or in the constructor, of course. Then you can make the array part one of the class's private vars (along with the last offset history), instead of static, if you want to be able to use multiple objects to track allocations of different things. BTW, what compilers is VS supposed to be portable to? Obviously not just GNU. :(

As long as we don't have a non-zero constant initializer for a static array, it won't take up any space in the exe.
Then that class could also take care of bitmasking in its inline [] operator...
speaking of which, doesn't vector<bool> take care of this? I thought that was required to be implemented as a bitmap...
Wouldn't it be better to make a serial number allocator class, instead of just a bitmap class with an overloaded [] operator? Too many overloaded operators makes code hard to read. Esp. bitmaps apparently have nasty [] overloading semantics. They seem to need a proxy reference helper class, since they need to return a reference to a bit. Yuck.

I don't really know STL, but here's what I found out looking stuff up (mostly via references from http://en.wikipedia.org/wiki/Bit_array): Boost has a find operator, but none of the STL stuff does. So you'd just have to loop over elements one at a time. If the first position you try is available, then it won't matter, but it's probably 64 times faster to test whole 64bit longs at a time than it is to loop over the bits. That is what makes my getUniqueSerial fast even if it has to search a decent fraction of the bitmap.

vector<bool> will be a bitmap when the compiler supports template partial specialization. (http://www.sgi.com/tech/stl/bit_vector.html). There's also a bitset<N> class (http://www.sgi.com/tech/stl/bitset.html), for bitsets of fixed length. It has more bit-op member functions, but its initializer is a single unsigned long, so I don't know if it's intended for use as a large bit array. bitset has a none() function, so you could use an array of bitset<64> efficiently, at least if the class doesn't have a lot of overhead. I wouldn't expect overhead; it doesn't have iterators or dynamic resizing or anything that would require storage except the bits themselves. So that would work. You'd still have to search in the bitset<64> for the first free element yourself, or call ffs(3) on bitset<64>.to_ulong(). ffs(3) is the only real portability problem in my code, and none of the STL classes solve that.

Ah, Boost has a dynamic_bitset class with a find_first(), and a even a find_next(size_type pos). So that would do the trick. It might even compile to something as efficient as my code, since it's a template, so the constant size can be evaluated at compile time.
http://www.boost.org/doc/libs/1_37_0/li ... itset.html And it has some comments about how nasty std:vector<bool> is, pretending to be a Container, when it's really not.... So since we have Boost anyway, this sounds like a good way to avoid ffs() portability problems. (It's only in POSIX-2001. and ffsl(3), which I actually use, is only a GNU extension. :( )

EDIT: Damnit, I just realized that since the find functions only find set bits, we'd have to use 1 == free, and initialize the bitmap to (almost) all ones, not all zeros. This will probably waste some RAM that otherwise would never have been touched, since the bitmap probably won't end up perfectly page-aligned. Oh well, it's still small, so it's not slow to init with:

Code: Select all

boost::dynamic_bitset<> bmap(MAXSERIAL/2 + 1, SERVER?0:1);
bmap.flip();
EDIT2: Double damn, boost's find_next() function doesn't use anything like ffs(3). It uses a shift-and-test loop, i.e. Boost's integer_log2. Oh well, at least it will be efficient getting to the right unsigned long, which is what could potentially be very slow.
I wonder if gcc is smart enough to compile that shift&test loop to a bsf instruction... Nope, it isn't.

EDIT3: here's a Boost version of the functions, which does look nice, but isn't as efficent (code size or speed) as my plain C version. I'd rather keep my plain C version, but only if it's not too hard to detect if we need to provide our own ffsl(3) implementation on non-GNU platforms. I guess just #ifndef __GNUC__.

Code: Select all

#include <boost/dynamic_bitset.hpp>

// init the first ulong here, resize to fill with ones later.
static boost::dynamic_bitset<> usedSerials(sizeof(unsigned long)*CHAR_BIT, SERVER?~0UL:~1UL);

ObjSerial getUniqueSerial(void)
{
	static int trynext = 0;
	unsigned int ret;

	ret = usedSerials.find_next(trynext);
	if (usedSerials.npos == ret) ret = usedSerials.find_first();  // we don't have a find operator that starts in the middle and loops
	if (usedSerials.npos == ret){
		cerr<<"**** Bad problem: Ran out of unique object serial numbers; returning 0 ****"<<endl;
		return 0;
	}
	trynext = (ret+1)%(MAXSERIAL/2 + 1);
	usedSerials[ret] = 0;

	if (SERVER) ret |= (MAXSERIAL/2 + 1); // set the high bit on server serials
	return ret;
}

void freeSerial(ObjSerial s)
{
  // discard the high bit regardless of previous setting.  If we want to catch the server trying to free client serials, maybe just subtract instead.
	/* if (SERVER) */ s &= ~(MAXSERIAL/2 + 1);  // no harm in always masking off the high bit
	usedSerials[s] = 1;
}

int main(void)
{
	usedSerials.resize(MAXSERIAL/2 + 1, 1);
	...
}
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
ace123
Lead Network Developer
Lead Network Developer
Posts: 2560
Joined: Sun Jan 12, 2003 9:13 am
Location: Palo Alto CA
Contact:

Re: stuck in getUniqueSerial

Post by ace123 »

I like that boost version more.

I think it also ought to move outside of vsfilesystem.cpp... I suppose it probably belongs in unit_generic.cpp along with everything else.
peter
Bounty Hunter
Bounty Hunter
Posts: 165
Joined: Sun Feb 11, 2007 3:40 am
Location: Halifax, NS, Canada

Re: stuck in getUniqueSerial

Post by peter »

ace123 wrote:I like that boost version more.
ok. It doesn't have any portability problems, so that's probably a good thing. I'll submit a patch for Boost to make it use ffsl() internally, maybe...

What about the lack of freeing of serial numbers? I keep asking, but you haven't yet addressed the only real bug here that needs fixing!
"The gods confound the man who first found out how to distinguish the hours!
Confound him, too, who in this place set up a sundial, to cut and hack my day so wretchedly into small pieces!" -- Plautus, 200 BC
Post Reply