Question On Need For Critical Sections

Recently I developed an ODBC Tutorial which I havn't published/uploaded to anywhere yet, as I need to work out a few kinks. The major problem I'm having is related to threading. The way the app works is when a button is clicked the app connects to Sql Server Express or MSDE (if installed), creates a simple database with a few fields, inserts a few records, then dumps the data to an output screen the program creates. All well and good.

In the button click procedure I use _beginthread() and all the various processing involving creating a database, counting records, inserting records, etc., is carried out in procedures called from the thread function. The reason I used threading is that I couldn't get the hourglass cursor to remain until the data became visible in the output screen. I believe there is asynchronous processing going on within the various ODBC & Sql Server layers and this fauls up the synchronization with my main thread's use of the hourglass cursor and arrow cursor and so on. Using the seperate thread for the database processing solved the problem very well.

Anyway, I found that I was not able to have the various database processing routines work correctly unless I enclosed some of the processing within critical sections. This might not seem strange to you but the fact is there is no global data in the application. My understanding of the need for critical sections is that if data is global/static to an application, two seperate threads might possibly access memory that is in an unstable state and faul things up. However, all the data being inserted into tables is stored in local stack variables within the insert procedure. Likewise with data being extracted. This is what has me confused. What I have found is that without the critical sections I can't get the program to run successfully. Problems occur in somewhat random locations.

The plot even thickens somewhat. I converted the C++ program over to the PowerBASIC language. I code in that language exactly the same as I do in C++, that is, pure Sdk style with no MFC, .NET or anything like that. The PowerBASIC program was just about a line for line exact translation. Anyway, believe it or not the PowerBASIC program runs perfectly using threads and no critical sections. My quandry is why won't almost the same program work without critical sections in C++? And not with just one compiler either. It works the same with Dev-Cpp, VC++ 6, VC++9, and CodeBlocks.

I've asked this question in somewhat differnt form over at daniweb and can't seem to get any response or answer.

http://www.daniweb.com/forums/thread221523.html

That is why I'm trying here. I signed up here quite some time ago but havn't been too good of a member unfortunately. I could change that though!

Anyway, I really hope someone could shed some light on this for me. I guess the general question is...Under what conditions are critical sections necessary, and when might they be necessary when there is no global/static data in an app to be processed?
Last edited on
I found that I was not able to have the various database processing routines work correctly unless I enclosed some of the processing within critical sections.


Are you accessing things from the main thread while the background thread is processing them?
You need critical sections (or mutexes) when you could possibly have two threads, one writing and the other reading or writing on the same data at the same time.
I'm as stupid as they come. My d--- problem isn't threads at all! After all the years I've been programming Windows I don't even know how to change the mouse cursor. Please kick me or something!

I'll figure this blasted thing out if its the last thing I do!!!!
> Recently I developed an ODBC Tutorial

What for ? ODBC is 17 years old .
There are tons of complete samples for ODBC in C and C++ in MSDN (SDK, KB, doc, etc), published since 1993.
You'll never find better than Microsoft code...
closed account (z05DSL3A)
You'll never find better than Microsoft code...

That sent a shiver down my spine. ;-)
hehe. I don't know if you'll never find better, but if you do, I agree that it will be a very hard thing to find. Microsoft has a LOT of talent in its roster.

What for ? ODBC is 17 years old .
There are tons of complete samples for ODBC in C and C++ in MSDN (SDK, KB, doc, etc), published since 1993.
You'll never find better than Microsoft code...


Perhaps you're right. The reason I'm doing it is I just broke down here a few weeks ago and bought Visual Studio 2008 Pro, and I wanted to tackle something somewhat major to get used to using it. I've been using VC6 for ages it seems, and thought it time to upgrade. The other thing I've been fighting for what seems like forever is UNICODE. I've finally decided to give up and give in to it and use TCHAR and all the truely horrible ugly macros in tchar.h. I'm just worn out and sick and tired of fighting it.

With regard to my initial post about the critical sections, I've been at it in the pits slugging it out with the app and I've determined for absolute certainty that my problem has nothing to do with threads or critical sections.

As I mentioned briefly in my initial post my whole use of threads and critical sections was prompted by my complete inability to successfully create an hourglass cursor during the split second or so that database transactions were occurring. So far this has defied every attempt of mine. And I'm not a beginning Win32 coder by any means. About 10 years ago I had a run in with this problem and I recall solving it I believe by handling the WM_MOUSEMOVE message and calling SetCursor() in that processing, and I believe I was using a static or global flag to test when to show the hourglass or the arrow. I remember thinking at the time that it was pretty ridiculous and awkward, and there had to be a better way; however, I recall it did work, and I never persued the matter further.

The reason I expect I havn't had a run in with this problem sooner is that for desktop app development I almost exclusively use PowerBASIC as it allows me to be much more productive than with C++. With that language though I do strictly Api Sdk coding. However, in that language they have a proprietary keyword 'MousePtr' where you can set it to any of the predefined equates such as hourglass=11 or arrow=1 and it works perfectly. That's why my PowerBASIC program is working and the C++ one not - it isn't anything to do with threads as I first thought.

Where I do a real lot of C++ development though is in Windows CE. And in those Apis there is a similiar situation as with PowerBASIC. There are predefined functions that can be called that are unique to CE that control the shape of the mouse cursor, and they work too.

So, I'm going to fight this today, tomorrow - however long it takes to beat it. I'm going to search the internet, study the docs, experiment - whatever it takes to beat it and however long it takes. If some kind soul out there wants to take pity on my poor stupid miserable self and tell me how to do it I am at a point beyond anger and humiliation and will gratefully accept with absolute humbleness and gratitude the secret hidden combination of Api calls that will turn a cursor into an hourglass and maintain it in that state untill in the fullness of time I see fit to turn it back into an arrow.

I knew this was going to be a problem before I started this project (remembering my experience of ten years ago), and so I studied the docs somewhat carefully on cursors, and noted that they were saying that when the mouse is moved the OS draws the cursor specified for the class when the class was registered, i.e., the hCursor member of the WNDCLASSEX struct. I further noted that this attribute of the window class could be changed using the SetClassLong() function with GCL_HCURSOR as the second parameter, and a valid HCURSOR as the third parameter. This led me to believe I might be able to successfully do something like this...

void TimeConsumingProcedure(lpWndEventArgs Wea)
{
HCURSOR hHourGlass, hArrow;

hHourGlass=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass);
//Do Time Consumming Processing
//..................................................
//Now Done - Show Arrow Cursor
hArrow=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass);
}

I even wrote a whole program to check it out. Here is that program. All there is is a main form with two buttons on it. When you click the top button the cursor is turned into an hourglass, and when you click the second back into an arrow...

//MouseCursors.h
#define IDC_BTN_HOURGLASS 1500
#define IDC_BTN_ARROW 1505

typedef struct WindowsEventArguments
{
HWND hWnd;
WPARAM wParam;
LPARAM lParam;
HINSTANCE hIns;
}WndEventArgs, *lpWndEventArgs;


struct EVENTHANDLER
{
unsigned int Code;
long (*fnPtr)(lpWndEventArgs);
};


//Now Main.cpp
//Main.cpp
#include <windows.h>
#include <tchar.h>
#include "MouseCursors.h"
EVENTHANDLER EventHandler[3];


long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
HWND hCtl;

Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
hCtl=CreateWindow(_T("button"),_T("Show Hourglass Cursor"),WS_CHILD|WS_VISIBLE,45,25,200,30,Wea->hWnd,(HMENU)IDC_BTN_HOURGLASS,Wea->hIns,0);
hCtl=CreateWindow(_T("button"),_T("Show Arrow Cursor"),WS_CHILD|WS_VISIBLE,45,70,200,30,Wea->hWnd,(HMENU)IDC_BTN_ARROW,Wea->hIns,0);

return 0;
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
HCURSOR hCursor;

switch(LOWORD(Wea->wParam))
{
case IDC_BTN_HOURGLASS:
hCursor=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd,GCL_HCURSOR,(long)hCursor);
break;
case IDC_BTN_ARROW:
hCursor=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd,GCL_HCURSOR,(long)hCursor);
break;
}

return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
if(MessageBox(Wea->hWnd,_T("Do You Wish To Exit This App?"),_T("Exit Check?"),MB_YESNO)==IDYES)
{
DestroyWindow(Wea->hWnd);
PostQuitMessage(WM_QUIT);
}

return 0;
}


void AttachEventHandlers(void)
{
EventHandler[0].Code=WM_CREATE, EventHandler[0].fnPtr=fnWndProc_OnCreate;
EventHandler[1].Code=WM_COMMAND, EventHandler[1].fnPtr=fnWndProc_OnCommand;
EventHandler[2].Code=WM_CLOSE, EventHandler[2].fnPtr=fnWndProc_OnClose;
}


long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WndEventArgs Wea;

for(unsigned int i=0;i<3 ;i++)
{
if(EventHandler[i].Code==msg)
{
Wea.hWnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
return (*EventHandler[i].fnPtr)(&Wea);
}
}

return (DefWindowProc(hwnd,msg,wParam,lParam));
}


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
TCHAR szClassName[]=_T("Form1");
WNDCLASSEX wc;
MSG messages;
HWND hWnd;

AttachEventHandlers();
wc.lpszClassName=szClassName; wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX); wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); wc.hInstance=hIns;
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION); wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW; wc.cbWndExtra=0;
wc.lpszMenuName=NULL; wc.cbClsExtra=0;
RegisterClassEx(&wc);
hWnd=CreateWindow(szClassName,_T("Form1"),WS_OVERLAPPEDWINDOW,300,200,300,200,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}

return messages.wParam;
}

After I wrote this program a few weeks ago I thought I had the problem solved. But alas, no!




Last edited on
I don't see an event handler for the WM_SETCURSOR notification, so I assume that you get the default behavior provided by DefWindowProc.

DefWindowProc will set the cursor to be whatever you set for the class. Well, it says "or to the registered class cursor if it is in the client area". See http://msdn.microsoft.com/en-us/library/ms648382%28VS.85%29.aspx.

So you seem to be in the right track by using SetClassLong(), but I think the problem is that nobody (you or the system) is calling SetCursor().

Suggestion: Call SetCursor() after SetClassLong().

Better suggestion (I think, not sure): Handle WM_SETCURSOR at the form level. Use a global HCURSOR variable that holds the cursor handle of the cursor you currently want. Then, before starting the long procedure, set the global variable to the wait cursor, and then SendMessage(WM_SETCURSOR) to the form. This way you don't have to use SetClassLong() and depend on the generosity of DefWindowProc.
Last edited on
....continued

When I attempt to use this technique in my ODBC code it fails. It fails completely. It fails completely and totally. It fails not only completely and totally, but diabolically and with evil intent and malice. The ODBC code creates an Sql Server database, creates a table within the database, inserts records into the table, then dumps the table to an output screen. So there are some somewhat time consuming processes involved and a wait cursor is needed generally for a second or two. Here is the actual button click procedure including the now unneeded thread creation function which actually does the above described activities with my debug code removed...

void btnSqlServerExpress_OnClick(lpWndEventArgs Wea)
{
HCURSOR hHourGlass,hArrow;
unsigned long hThread;
TCHAR szVar[32];
HWND hOutput;
DWORD hWait;

hHourGlass=LoadCursor(NULL,IDC_WAIT); //get hourglass cursor
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass); //set it to class struct
_tcscpy(szVar,_T("ODBC Demo With Sql Server"));
hOutput=CreateWindow //create output window
(
_T("frmOutput"),
szVar,
WS_OVERLAPPEDWINDOW|WS_VSCROLL,
200,500,775,275,0,0,
GetModuleHandle(NULL),
Wea->hWnd
);
hThread=_beginthread(SqlServerThread,0,0); //this thread function does
hWait=WaitForSingleObject((HANDLE)hThread,INFINITE); //all the database stuff serially.
ShowWindow(hOutput,SW_SHOWNORMAL); //When its done show the output
hArrow=LoadCursor(NULL,IDC_ARROW); //window an remove hourglass
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hArrow);
}

The above code howsoever logical it might be doesn't work. The hourglass cursor never shows. In fact, its fails so absolutely diabolically that if you comment out the last two lines of code in the above procedure that turn the hourglass (the one you never see) back into an arrow, what will happen when you click the button is that the heavy duty database processing will begin (you can hear the churning of the harddrive and stuff), and the mouse cursor will continue to be an arrow. Then, the very second the output screen becomes visible and the hourglass that never appeared should become an arrow once again, then the blasted cursor becomes an hourglass until you end the program!!!!!!!! Diabolical!!!!

I actually had this program working perfectly by having the ...

ShowWindow(hOutputScreen,SW_SHOW) ;

at the tail end of the thread procedure along with the code to change the mouse cursor. Now, I'm pretty sure that with threads one shouldn't be call calling GUI related functions on the main thread represented by my main window. I forgot about that when I coded it. However, it didn't crash & worked perfectly!!!! The second one clicked the button to start the Sql Server processing the cursor would turn into an hourglass and would maintain that state until the output window appeared. I left the program that way and thought it was finished and I had beaten the cursor problem.

Then I moved on to the PowerBASIC version of the program and I kept getting crashes at the end of my thread procedure until it dawned on me I had that ShowWindow() call in a worker thread that shouldn't be calling GUI functions on the main thread. I moved the ShowWindow and cursor management functions back to the end of the button click procedure and everything worked perfect in the PowerBASIC app, cursor and all. Of course with PowerBASIC I was just doing a 'MousePtr 11' to show an hourglass cursor and 'MousePtr 1' to turn it back to an arrow, and the guy who wrote the PowerBASIC compiler - Mr. Bob Zale of TurboBASIC and Borland fame - most certainly knows how to control mouse cursors - something I most certainly don'!

So then I decided I'd better fix the C++ program and put the ShowWindow() and mouse cursor calls back in the main thread where they belong - and that brings you up to where I'm at now. That completely broke the previously functioning cursor code in the C++ program. Somehow or other compensating errors were taking place that was causing the program to work exactly as I wanted concerning the mouse, in spite of the issue of having grave errors in my thread code. That's why I thought I had a thread problem. I just couldn't understand why critical sections were needed in an app with no global data and serial database access. So that's the story.
Thanks webJose! You posted between my long diatribe! If I weren't writting so much here I'd probably have the blasted thing solved by now. As you might be able to tell - the thing's got me in high gear. I don't usually get so excited. I'll be at it now though trying your suggestions and other things I thought of. I need to beat this and put it behind me.
I find this line from the Sdk docs on the SetCursor() function interesting...

"If your application must set the cursor while it is in a window, make sure the class cursor for the specified window's class is set to NULL. If the class cursor is not NULL, the system restores the class cursor each time the mouse is moved."

So I'm wondering if I do a...

SetClassLong(hWnd, GCL_HCURSOR,(long)NULL);

on the hWnd followed by a single call to SetCursor(hHourGlass) if that might work. Time to try some stuff...


Ahhh! SUCCESS!!!

Here's what I've just found.

The secret combination is fairly small (as I thought it would be) and it doesn't require processing of the WM_SETCURSOR message. It is just as webJose said; simply call SetCursor(hHourGlass) right after the SetClassLong() call that sets the new cursor handle in the hCursor class struct. Here is the final working button click procedure...

void btnSqlServerExpress_OnClick(lpWndEventArgs Wea)
{
HCURSOR hHourGlass,hArrow;
unsigned long hThread;
TCHAR szVar[32];
HWND hOutput;

hHourGlass=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass);
SetCursor(hHourGlass);
_tcscpy(szVar,_T("ODBC Demo With Sql Server"));
hOutput=
CreateWindow
(
_T("frmOutput"),
szVar,
WS_OVERLAPPEDWINDOW|WS_VSCROLL,
200,500,775,275,0,0,
GetModuleHandle(NULL),
Wea->hWnd
);
hThread=_beginthread(SqlServerThread,0,0);
WaitForSingleObject((HANDLE)hThread,INFINITE);
ShowWindow(hOutput,SW_SHOWNORMAL);
hArrow=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hArrow);
SetCursor(hArrow);
}

The sequence simply boils down to three steps...

1) Get handle of cursor you want;
2) Set it in the REGCLASSEX struct with SetClassLong()
3) Call SetCursor(hDesiredCursor) to change cursor.

Unbelievable amount of time & frustration lost on this. Somehow I just couldn't believe I had to add another message handler ( WM_SETCURSOR ) to my app to make this work (as I had apparently done years ago), and to make matters worse create a global flag to specify the status I wanted. I know there are two camps concerning globals; 1) those who think they are OK if not over used; and 2) those who forbid them; I'm basically in the latter camp. That's why I didn't want to go back to what I had done years ago, although I must admit I was getting a bit desperate! And anyway, how was PowerBASIC making it work with their neat little proprietary 'MousePtr 11 / MousePtr 1' thingy??? I got to thinking...how can the compiler figure that out to insert its own WM_SETCURSOR handler over and above my handlers in their compilation of my Sdk source? It just wasn't making sence to me.

So you've really helped me out webJose! I'd have probably figgured it out in time but the first thing I tried when I got up from writting these long posts was your suggestion on calling SetCursor() right after the SetClassLong() call and that did it. Actually, the first time I tried it I had a WM_SETCURSOR handler in the code because I was up to late last night fighting the blasted thing without success, so I left that in and it worked. When I saw that I thought, "Well, lets now see what happens with the WM_SETCURSOR handler removed and it worked perfectly. That's why I'm saying the key is those three steps I've mentioned above. In fact, its probably worth combining them into a little utility function to mimic what PowerBASIC does such as...

void MousePtr(HWND hWnd, HCURSOR hDesiredCursor)
{
SetClassLong(hWnd,GCL_HCURSOR,(long)hDesiredCursor);
SetCursor(hDesiredCursor);
}

Well, finally! Now I've got to decide what to do with the stupid thread code in both apps! Leave it in without critical sections, or just rip it out and call all the ODBC stuff right from the button click procedure?

The app is really cool. Had been wanting to do it for quite some time, in spite of the fact that there may be a million ODBC demos out there. If nobody wants it I at least became familiar with my VC++ 9 and now understand how to get an hourglass cursor in Sdk C++ code.

The compiled C++ code for this app which dumps Sql Driver info, an Excel spreadsheet, creates & dumps Access and Sql Server databases, plus displays all the info in independent scrollable output screens only comes to 36K. It contains my own string class and ODBC wrapper class.

I was absolutely amazed the executable came out a bit smaller than the PowerBASIC executable with VC++9. This is the first time I've ever seen this in any desktop Windows programs. PowerBASIC always beat the h--- out of any C or C++ compilers. I think it might have something to do with the recent incorporation of the msvcrt.dll into the Windows operating system. PowerBASIC still supports Win95-98 executable format, so probably adds some msvcrt code to the exes. I see VC++9 doesn't support those older exes.
Last edited on
It is understandable that you went for my first suggestion considering the back-then state of your source code. The first suggestion was a matter of adding 2 lines. But it is not the GENERAL way to go, and you do wrong in assuming this should be the way you should do you applciation always.

The second option is the way to go (or whatever variation you want to implement). Your application is probably OK with this change because you probably instantiate only one window of that window class. But if you instantiate more windows, all windows will inherit the new cursor based on what is happening in just one of them. Why? Because DefWindowProc() will use the cursor in the class to set the mouse cursor. The class. Not the specific window, but the window class that defined it.

The end result: If you put a wait cursor in a window of yours, other windows of the same class will start showing the wait cursor.

The bottom line of my suggestion is: Don't change the cursor for the window class. Do it differently.

My second suggestion is how I would have done it. And if you think about it, it is no more complex than the first solution. See my version below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

HCURSOR _thisWindowCursor; //Per-form variable.
...
void MousePtr(HWND hWnd, HCURSOR hDesiredCursor)
{
    _thisWindowCursor = hDesiredCursor; //Instead of SetClassLong()
    SetCursor(hDesiredCursor);
}
...
//Inside the Window Procedure for the form
case WM_SETCURSOR:
    SetCursor(_thisWindowCursor);
    return TRUE;
    break;


If you compare them, you'll see that I gain per-form cursors with just a few extra lines of code, and the MousePtr() function is still 2 lines.
Your point is well taken webJose, and thanks again for the additional thought & comments. I actually had thought of that too; that is, as many instances of the main program that were running would all show the hourglass if one of the buttons on one of the forms was clicked; However, I was so aggrevated with the problem I was having; that is, I couldn't get it to work at all, that I wasn't going to worry too much about 'what ifs' and fine points.

If I ever write a desktop app in C++ however where it would make sense or even be possible to instantiate multiple instances of the app, I think I'd be inclined to use your method and store your _thisWindowCursor variable in a struct a pointer to which I'd store in some .cbWndExtra byte or Window Property memory. It would have been a bit more convenient if the cursor would have been stored as part of a window's properties than as part of the class properties, but there again, that isn't how MS did it, so we have to live with it the way it is.

All I know is I'm glad I got it fixed. It ended up being one of those problems I thought would take an hour to solve and I ended up losing a whole day or two on it. Actually, more; because I mis-diagnosed the problem I was having as being something related to threads, I spent days writing and researching thread code only to find out that threading issues were more of a symptom of my problem than the main cause. Oh, well! I needed practice with threads anyway!
Topic archived. No new replies allowed.