News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Use CreateThread in MASM Program

Started by DavidB, November 21, 2017, 01:23:03 PM

Previous topic - Next topic

DavidB

I am writing a program in Microsoft Assembler and making use of Kip Irvine's libraries, and I need to run one of my procedures in a separate thread. This procedure is responsible for drawing in the console, and basically, I would like to run the procedure in an infinite loop in a separate thread while allowing the user to perform other actions in the primary thread.

I have read that it is possible to do this using "CreateThread", but I have not found any documentation on this that is detailed enough for it to make sense. I have tried implementing a separate thread as is done here, but I was not able to get it to assemble. Could someone explain to me how this can be done?

jj2007

Quote from: DavidB on November 21, 2017, 01:23:03 PMI have read that it is possible to do this using "CreateThread", but I have not found any documentation on this that is detailed enough for it to make sense.

There is a tool called "Google" that can provide you with "About 1,600,000 results" on "CreateThread". If that is not enough, there is a second tool called "Forum search" that, if you can find it (e.g. by clicking here), will give you 24 threads dealing with "CreateThread". All this requires a skill that has become rare in the era of YouTube tutorials, it's called "reading".

hutch--

David,

With Irvine code I cannot help you but using CreateThread means looking up its arguments and creating a new procedure that the new thread starts in. The old Win32.hlp has the reference material but if you don't have that you can look up the API in MSDN and it will refer you to the format of the procedure which is a single argument. In 32 bit its a DWORD, in 64 bit its a QWORD. If you need to pass a lot of data (more than 1 arg) you pass the address of an array OR structure that contains the data you want to pass.

dedndave

CreateThread is pretty simple
i quite often use it with most of the arguments = 0

    INVOKE  CreateThread,0,0,MyThread,0,0,0

the 1st, 2nd, and 5th arguments are rarely needed
the 4th and 6th arguments are of possible interest....

the 6th argument may be used to point to a variable that receives the thread ID
the 4th argument may be used to pass an argument to the thread

.DATA?

dwThreadId dd ?

.CODE

    INVOKE  CreateThread,0,0,MyThread,12345678h,0,offset dwThreadId

.
.
.

MyThread PROC

    mov     eax,[esp+4]                ;EAX = 12345678h
.
.
.
    INVOKE  ExitThread,eax

MyThread ENDP


at the bottom of this page are a few related functions of interest...

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx

avoid using TerminateThread
avoid using a MyThread proc with a stack frame (no arguments or local variables)

aw27

Quote from: dedndave on November 22, 2017, 01:53:53 AM
avoid using a MyThread proc with a stack frame (no arguments or local variables)
Not true. They have 1 argument and you can use local variables.

Actually my recent example builds hundreds of threads with locals.
http://masm32.com/board/index.php?topic=6694.msg71668#msg71668

dedndave

we've had this discussion before - lol
it's probably ok (ExitThread is supposed to release the stack space)
but, when advising a beginner, i prefer to err on the side of caution
it's just simpler to advise him to write the code without a masm-generated frame

jj2007

From the MSDN Creatings Threads example:DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
    HANDLE hStdout;
    PMYDATA pDataArray;

    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;

    // Make sure there is a console to receive output results.

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if( hStdout == INVALID_HANDLE_VALUE )
        return 1;

    // Cast the parameter to the correct data type.
    // The pointer is known to be valid because
    // it was checked for NULL before the thread was created.

    pDataArray = (PMYDATA)lpParam;

    // Print the parameter values using thread-safe functions.

    StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"),
        pDataArray->val1, pDataArray->val2);
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);

    return 0;
}


It has a stack frame, it has local variables, and it ends simply by RETurning. Note this phrase:
QuoteIt is risky to pass the address of a local variable if the creating thread exits before the new thread, because the pointer becomes invalid.

It refers to the proc that invokes CreateThread, not to MyThreadFunction. Here is a complete example:include \masm32\include\masm32rt.inc

.code
Wait4Key proc arg
Local v1
  .Repeat
invoke Sleep, 1
invoke GetKeyState, VK_SHIFT
test ah, ah
  .Until Sign?
  print "FINALLY!"
  ret
Wait4Key endp
start:
  invoke CreateThread, 0, 0, Wait4Key, 0, 0, 0
  MsgBox 0, "thread is still running", "bye:", MB_OK
  exit

end start

aw27

There are a number of precautions to take when using multiple threads, and it also depends on the type of application we are dealing with (console, windows, service, etc.)
In my quoted example, the main issue I had to deal with, as far as threading was concerned, was using "printf" from multiple threads. printf is not thread safe. While in the SDK example that I took as inspiration, they simply did not use the printf, obviously for that reason, I decided to keep with it but allow access to only 1 thread at a time by using a semaphore.

jj2007


DavidB

Quote from: jj2007 on November 21, 2017, 06:38:13 PMThere is a tool called "Google" that can provide you with "About 1,600,000 results" on "CreateThread". If that is not enough, there is a second tool called "Forum search" that, if you can find it (e.g. by clicking here), will give you 24 threads dealing with "CreateThread". All this requires a skill that has become rare in the era of YouTube tutorials, it's called "reading".
Congratulations. You have officially contributed nothing useful to this conversation. Now please, go away and let people actually be helpful.  ;)

Quote from: hutch-- on November 22, 2017, 01:37:31 AM
David,

With Irvine code I cannot help you but using CreateThread means looking up its arguments and creating a new procedure that the new thread starts in. The old Win32.hlp has the reference material but if you don't have that you can look up the API in MSDN and it will refer you to the format of the procedure which is a single argument. In 32 bit its a DWORD, in 64 bit its a QWORD. If you need to pass a lot of data (more than 1 arg) you pass the address of an array OR structure that contains the data you want to pass.
I think a majority of the issue that I was having at the time of writing this post was the fact that I was limiting myself to the Irvine libraries. I did not realize that I would have to use the MASM32 SDK to achieve this. That's not really a problem though. Thanks for providing an explanation that is easier to understand.

Quote from: dedndave on November 22, 2017, 01:53:53 AM
CreateThread is pretty simple
i quite often use it with most of the arguments = 0

    INVOKE  CreateThread,0,0,MyThread,0,0,0

the 1st, 2nd, and 5th arguments are rarely needed
the 4th and 6th arguments are of possible interest....

the 6th argument may be used to point to a variable that receives the thread ID
the 4th argument may be used to pass an argument to the thread

.DATA?

dwThreadId dd ?

.CODE

    INVOKE  CreateThread,0,0,MyThread,12345678h,0,offset dwThreadId

.
.
.

MyThread PROC

    mov     eax,[esp+4]                ;EAX = 12345678h
.
.
.
    INVOKE  ExitThread,eax

MyThread ENDP


at the bottom of this page are a few related functions of interest...

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx

avoid using TerminateThread
avoid using a MyThread proc with a stack frame (no arguments or local variables)
Thank you! I had actually seen the MSDN page that you are referring to earlier, and read the entire thing, but your short, concise explanation was much more beneficial to me.

Quote from: aw27 on November 22, 2017, 06:40:11 AM
There are a number of precautions to take when using multiple threads, and it also depends on the type of application we are dealing with (console, windows, service, etc.)
In my quoted example, the main issue I had to deal with, as far as threading was concerned, was using "printf" from multiple threads. printf is not thread safe. While in the SDK example that I took as inspiration, they simply did not use the printf, obviously for that reason, I decided to keep with it but allow access to only 1 thread at a time by using a semaphore.
The program that I am writing is a console application. Basically, I want to have a procedure in a separate thread that is used for repeatedly drawing in the console every x seconds, and in the main thread, have a procedure which repeatedly waits for keyboard input from the user using Irvine's "ReadChar."

jj2007

Quote from: DavidB on November 22, 2017, 11:48:42 AM
Quote from: jj2007 on November 21, 2017, 06:38:13 PMThere is a tool called "Google" that can provide you with "About 1,600,000 results" on "CreateThread". If that is not enough, there is a second tool called "Forum search" that, if you can find it (e.g. by clicking here), will give you 24 threads dealing with "CreateThread". All this requires a skill that has become rare in the era of YouTube tutorials, it's called "reading".
Congratulations. You have officially contributed nothing useful to this conversation.

And again, you confirm that you are stinking lazy, unwilling to read the FM, and waiting for people to spoon-feed you (otherwise you would have noticed that I supplied a full example above). You won't get far with this attitude. Btw there is a "no homework" rule in this forum.

DavidB

Quote from: jj2007 on November 22, 2017, 12:05:49 PMAnd again, you confirm that you are stinking lazy, unwilling to read the FM, and waiting for people to spoon-feed you (otherwise you would have noticed that I supplied a full example above). You won't get far with this attitude. Btw there is a "no homework" rule in this forum.
I saw the whole example. I just did not notice that it was from you. That was actually my mistake, so I apologize.

As for the "no homework" policy, I am well aware of it. While this question is related to a homework assignment, the threading aspect of my project is not a requirement. For my final project, I can create anything I want as long as I satisfy a certain set of requirements, but I am allowed to add extra stuff if I want. I have already satisfied the project's minimum requirements to get the grade. This multi-threading business that I am trying to do would just be a nice touch. So please don't make assumptions. And most importantly, remember that we all start somewhere. You were a noob once, too. Please don't forget that.  ;)

hutch--

You solve the calling proc exiting before the thread starts by using a spinlock to delay the caller's exit until the thread has successfully started and the argument content has been passed to it. It tends to only be a problem if the caller procedure is being hit reasonably fast, a longer procedure that does not exit quickly does not have the problem.

felipe

Quote from: DavidB on November 22, 2017, 12:13:21 PM
remember that we all start somewhere. You were a noob once, too. Please don't forget that.  ;)
Not true actually, times changes, it would be sad that every thing would be the same as years ago. I guess that teachers don't put emphasis on that fact, but certanly things are different (probably every time). So take note: don't make wrong assumptions, if you want to actually learn from this forum you should respect some rules.
Now, of course you can think what you want, i'm just telling that generalizations like the one above is not really applicable. So the simple, easy and good thing would be saying: "We have all been noob once, but very differents noob".
:biggrin:

jj2007

Quote from: hutch-- on November 22, 2017, 12:31:10 PMYou solve the calling proc exiting before the thread starts by using a spinlock to delay the caller's exit

Right, you can do that - WaitForxx etc. But we need to distinguish two important uses for CreateThread imho:

1. You are not sure if the threadproc succeeds at all, so you use a thread and a timeout to avoid a hanging application -> spinlock, WaitForxx;

2. You know that the thread proc will take some time, but you don't want to be blocked -> completely asynchronous process, the thread starts but you exit immediately from the calling procedure, and when the thread has finished, you'll get a notification via WM_PRIVATE_WHATEVER.

In case 1, you can pass the addresses of local variables to the thread, in case 2 you cannot.