How to write an infinite recursion … elegantly

February 2nd, 2011

I’m working on a DB Lookup Combo with filter, sorting and most important: instance search (like winamp).

So, I figured I’d use a TClientDataSet as a proxy dataset for my combo and just descend from TDBLookupComboBox

All nice and dandy until the part where I have to respond to changes in the original dataset and update my proxy one. As always I did it like


dataset.disableControls;

try

dataset.first;

while not dataset.eof do blabla

finally

datasert.enableControls;

end;

The component was integrated into a financial application and for some reason I got the freeze, but only for a few seconds. For the life of me I couldn’t figure out what it was wrong, especially because from about 10 similar adotables and about the same number of forms, similar in construction and components, it only happened with one and only one. MadExcept by itself was not useful as setting the freeze timeout to 2 second it didn’t fire. Then I remembered madTraceProcess.exe and that did it: it showed the issue being in my component. A few breakpoints in the right spot (trial and error) got me seeing the recursion happening on the dataset.enableControls line. A few more minutes of debugging proved that the issue was caused by that usage as I was doing it in the DataLink.DataSetChanged method which is called when the dataset “changes”. But if you look through the code in DB.pas you’ll notice that the datasetChanged is called in a lot more other situations, but most notably in EnableControls.

Long story short, this is probably the “best” infinite recursion I’ve ever seen till now. and it was caused because my assumption that datasetChanged is called when the dataset changes. Wrong, it is called in EnableControls as well.

Related posts

Facebook? uhm….

January 26th, 2011

So … facebook. Right. Social Network. The all new trendy thing to do. I have to tell you, after seeing all the hi5-like sites here in my country and seeing what kind of people join it and use it I still am pretty reluctant toΒ  using such … “sites”. basically, it’s just a damn dating site with lots of features. The fact that there on your profile along with your personal information you have options for “Interested In: women/men” pretty much proves it.

I won’t get into all the stuff with security, privacy (I hope you know you don’t have privacy there for a lot of stuff. FB application developers know it. You should too, just google). I won’t even say anything about how idiotic the concept is, even if it brings in tons of money (hey, the world is full of idiots) but I will tell you this: the developers there are STUPID.

So, I figured I should create an account. Not because I need or want it but because it makes a pretty damn good launching pad for my stuff (once I get to put it up). As with twitter, I will be doing mostly nothing, just to see if other people “see me” and find me interesting for what I share not because I added them to my “list”.

Ok, so back to why I post this: the developers are idiots. first page, enter email address. So I enter the one I always do: root@ciuly.com no secret there. The error message:Β  “Please enter a valid email address.”

They are not the first to reject <<root@*>> email addresses but hey, idiots, that IS a valid email address.

so I went with my second choice: ciuly@ciuly.com

Now, the error was “There is an existing account associated with this email.”

You can imagine, I was like WTF???

And indeed, I had an account made.Β  Seems I made it in december 2009. If I recall correctly, I needed to check out some people πŸ˜€ Same reason for which I have a hi5 account lying somewhere.

Anyway, the news is out. There will be some releases coming soon. Keep an eye out πŸ˜‰

Related posts

Row cannot be located for updating. Some values may have been changed since it was last read.

January 13th, 2011

Yet another one of those cryptic messages that basically says “you’re screwed”.

So what was I doing this time? Well, I had my mysql tables designed as having different fields as float. It just came natural to me that way. Everything went well until today when I had to modify that field on a record which had some fractional part (until now I did modifications but only to values with no fractional part) and boom. Took me a while to figure it out that it was the float type of the mysql table field that caused this because you see, float in mysql at least is not exact. It’s an approximation. That means that ADO will have a hard time to find hte modified record if you modify the value with a fractional part. It will actually raise ”

Row cannot be located for updating. Some values may have been changed since it was last read.”

Happy me. Solution? Just change the mysql column float into decimal(M,L). I use Decimal(15,2) as it works fine for my application. Yeah, have a laugh at all those people suggesting cursor type changes, ado property modifications and so on. People, first find THE CAUSE, and then find a way to fix it.

Related posts

Multiple Step Operation Generated Errors – Check each status value

January 12th, 2011

Yet another cryptic error with an unusual/weird test case and solution.

So, my setup:

– a table with a date field marked null/not null (tried both, same thing)

– an TADOTable to work with the above

– added all fields in the field editor

– a TDBDateEdit from RXLib with DefaultToday set to true

You make an append. You don’t touch the date as it’s set for today. You change everything else. You post. And you get the error:

“Multiple Step Operation Generated Errors – Check each status value”

Why? Well, thing is that when you make the append, the underlying TDataSet will set the state of itself to dsInsert, at which point TDBDateEdit will notice that a new record is added and set the date to today which makes the date field to be added to the internal modified field list of the TCustomADODataSet.

Now, the code runs further and calls DoOnNewRecord which clears the above mentioned internal modified field list and hence, when you do post, the date field is not in the list and hence it is not posted.

Now, since on the client side the field is set and has a value, on DB side the field was not sent and is either null or lots of zeros (if field is set not null) and hence they do not match (in this latter case delphi will also error because it cannot convert it to date. Not sure when this is done, but it is done). So, ADO will now raise this error BUT, the record was already added, just with an invalid date value. And if you are in the not null scenario and reload the grid, you will surely get the “ Data provider or other service returned an E_FAIL status.

There you go, yet another scenario for that cryptic error message πŸ™‚

Related posts

Data provider or other service returned an E_FAIL status.

January 10th, 2011

It just happened. Took me a few minutes to track it down. I’ll be short:

– my steup: mysql and delphi with ADO. a table with a date field set to not null

– on client side, I inserted a record and for some reason did not set the date. that went as lots of zero-s in mysql.

now, when loading that record, I get that message because lots of zeros are not a valid date value.

There you go, another explanation for the dreaded message.

Related posts

Properly refresh DBGrid without loosing current position

January 8th, 2011

This article on delphi.about.com shows one way: http://delphi.about.com/od/delphitips2008/qt/dbgrid_row_pos.htm

However, if you consider the following scenario, you’ll notice that one is not working right:

– initial recordset has 10 records

– position on the 9th

– delete first 7 records

– cursor should now be on the 2ndΒ  row.

Same goes if we add records in front of the current record.

So, how to do this correctly? The first impuls is to do something like:


b := ds.GetBookmark;

try

finally

if (b<>nil) and ds.BookMarkValid(b) then

try

ds.GotoBookMark(b);

finally

ds.FreeBookMark(b);

end;

end;

That looks all nice and dandy but:

– this will always position the selected row in the center of the grid. It’s pretty annoying if you do repetitive refreshing (like in a timer)

– if the current record is deleted … nothing happens. Of course if you happen to reposition the cursor (like using locate) in the code, then you will have some random re-positionings. Again, not nice.

So lets start with problem no 2.


RecNo := ds.RecNo;

b := ds.GetBookmark;

try

finally

if (b<>nil) and ds.BookMarkValid(b) then

try

ds.GotoBookMark(b);

finally

ds.FreeBookMark(b);

end

else if (recno<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;

Ok, so this solves the case when the current record is deleted. Still we have to fix the centering of the GotoBookMark.

Now, upon inspection of that function, we see that it actually does a Resync and passes rmCenter to it, which is our problem. So let’s simulate the GotobookMark without centering:


THackDataSet=class(TDataSet) // a nice "hack" so we can access protected members
end;

RecNo := ds.RecNo;

b := ds.GetBookmark;

try

finally

if (b<>nil) and ds.BookMarkValid(b) then

try

// Β Β Β Β  ds.GotoBookMark(b);

ds.CheckBrowseMode;
THackDataSet(ds).DoBeforeScroll;
THackDataSet(ds).InternalGotoBookmark(b);
ds.Resync([rmExact{, rmCenter}]);
THackDataSet(ds).DoAfterScroll;
finally

ds.FreeBookMark(b);

end

else if (recn<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;

Ok, now this almost solves the problem. Now, the row is repositioned to the last in the grid. Still not what we want. So what we can still do here is force the resync to take the active record the right one and not the one set by the InternalGotoBookmark.


THackDataSet=class(TDataSet) // a nice "hack" so we can access protected members
end;

THackDBGrid=class(TDBGrid)
end;

Row := THackDBGrid(grid).Row;// or THackDataSet(ds).ActiveRecord

RecNo := ds.RecNo;

b := ds.GetBookmark;

try

finally

if (b<>nil) and ds.BookMarkValid(b) then

try

// Β Β Β Β  ds.GotoBookMark(b);

ds.CheckBrowseMode;
THackDataSet(ds).DoBeforeScroll;
THackDataSet(ds).InternalGotoBookmark(b);
if THackDataSet(ds).ActiveRecord <> Row - 1 then
THackDataSet(ds).MoveBy(Row - THackDataSet(ds).ActiveRecord - 1);
ds.Resync([rmExact{, rmCenter}]);
THackDataSet(ds).DoAfterScroll;
finally

ds.FreeBookMark(b);

end

else if (recno<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;

This is almost perfect. There is still the same issue of positioning on the first/last record when the current record is deleted. I can’t confirm this yet as my test scenarios haven’t gotten that far but I *think* it will happen. And if it does, you can simply apply the same ActiveRecord thing to calculate the correct position for the MoveBy.

I’ll update this post if and when I get to kick this scenario.

http://delphi.about.com/od/delphitips2008/qt/dbgrid_row_pos.htm

Related posts

New anti-spam rules on forum

December 22nd, 2010

A few days ago I started receiving a bit too many new user registrations so … as usual, I did something about it.

Long story short, here’s the link: http://forum.ciuly.com/index.php?topic=318.0

Related posts

My alternative “concept” browser

December 19th, 2010

As I keep spitting fire on current browsers as you can read through my last posts since all of them are real resource hogs consuming memory and resources like crazy, I announced the possibility of creating my own concept browser, meaning that I will base it’s core on an existing one (embedded) and just implement my concept of how a real browser should handle resources.

My first project went up in smoke using TEmbeddedWb (from bsalsa) which is based on IE because with every tab switch the memory grew with around 3-5 MB. Then I tried some others, based on gecko but I ran into other problems PLUS memory leaks (although smaller).

So I got back to TEmbeddedWB because I got an idea: why not also destroy the object instance? This should force a complete memory de-allocation. Partially true.

Then I did some research and found the following:

// force memory to be really de-allocated: http://www.bsalsa.com/forum/showpost.php?p=2551&postcount=19
SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);
{FROM MSDN: -1, -1: If both dwMinimumWorkingSetSize and dwMaximumWorkingSetSize have the value (SIZE_T)–1, the function removes as many pages as possible from the working set of the specified process.}
// also equivalent to EmptyWorkingSet

Which indeed now showed a very nice grow of “only” 1 MB per tab BUT, this time depending on the contents of the tab. So if you load a 10 mb tab and then a 1 MB tab, or load a 10 MB and then a 20 mb, you still only loose 2 MB instead of 6+

So I figured if this really dropped the memory usage for the IE based project, how about trying the gecko one, despite the other bad “habits” it has. After fiddling with it a bit I got it working and the test results showed a pretty similar pattern in “leaking” as the one with IE. So at least from leaking point of view, the gecko and IE based projects are similar.

As a weird side-note, (I’m on D7 here and now) even after enabling the OleCtrls memory leak fix and recompiling EWB the memory leaks remained the same. True, that I didn’t use any interface property references which would explain this, but I thought that internally, the component might use them. Oh well, nothing is lost but nothing is gain either.

Since the mozilla activex control that I use for the gecko implementation is a bit old and unmaintained and lacks a lot of needed features for a browser, I will go with TEmbeddedWB in implementing a very skeletical and bare-bone and minimalistic but very light and resource friendly tabbed-browser.

And maybe I can finish it before the end of the year πŸ™‚

Related posts

Safari, yet another memory hog

December 19th, 2010

So, I installed safari … what was I thinking…

Clean install. Removed all “top sites” bullshit that loaded there. Let it to “stabilize” for maybe 3 minutes. Conclusion? I didn’t even load a page and it eats 164MB ram and 151MB VM = 315MB memory. Obviously, I just uninstalled it.

Are there any sane programmers out there capable of writing normal applications?

Related posts

Why Chrome browser really sucks

December 18th, 2010

I’ll probably repeat myself but here goes: EVERY single (major) browser today sucks.

So Chrome is now on the table specifically. I have 6 tabs open.:

– gmail

– ebay

– yahoo mail

– ebay

– a static html page on my site

– and http://www.coolest-birthday-cakes.com/tractor-cake.html

Now, there are 10 (TEN) chrome processes running with following memory consumption (ramn and VM:

– 1: 43MB/32MB

– 2: 49MB/36MB

– 3:Β  24MB/16MB

– 4: 30MB/20MB

– 5: 38MB/28MB

– 6: 7MB/3MB

– 7: 30MB/23MB

– 8: 26MB/17MB

– 9: 10MB/6MB

– 10: 19MB/11MB

Total: 10 process with 276MB ram and 192MB VM = 468 MB memory consumed. I repeat 6 (SIX) open tabs.

Firefox is currently eating up 54 open tabs and 178 MB ram and 354 MB VM = 532 MB memory.

That is a HUGE difference.

Also, when chrome updates, it downloads it’s entire kit and installs it “clean” also leaving the old installation folders there. So it also eats up harddrive space (not that I’m running low on that).

But just imagine, on my 2GB ram+2GB VM system, firefox and chrome alone eat up 1 GB. 25% of available memory. And I just fired up VMWare with a system allocated 384 MB ram and taht one eats up 437 MB ram and 17 MB VM so there you go: firefox + chrome + 1 VM eat up 37.5% of system resources. And I have 65 processes running right now amongst which are 4 IM applications, thunderbird for email, avast as AV and outpost as FW.

I’m going to ditch chrome (probably for good) pretty soon.

Ok, so I noticed opera 11 is out (for some time) so I thought I’d give it a spin. I opened 4 tabs:

– yahoo mail

– gmail

– feedburner

– internet banking from RBS

Memory consumption for opera is now 248MB ram and 253MB VM = 501MB memory… I opened another tab and the memory only grew with maybe 5 MB so this gets me thinking: what in the world does opera load in memory if 5 tabd consume aprox 30 MB memory? What’s the rest of 470MB ???? Especially since opera was running for less than 5 minutes and the settings (AFAIK) are set for low memory consumption (including disabled memory cache)

Seriously now, is it just me or todays browsers are really just very lame memory/resource hogs?

Related posts