|
It's a rare C program, no Events 
|
|
|
|
|
What is a "rare C program?"
Is this on Windows?
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|
No, its on a 32 bit micro controller, no OS.
|
|
|
|
|
Then you haven't threads, have you?
Are you talking about interrupts?
|
|
|
|
|
no, no interrupts, it's a simple scheduler using while(1).
and one cycle is 10 ms , one cycle is 5 ms.
task A in 10 ms cycle, task B in 5 ms cycle.
|
|
|
|
|
But have you threads? Microcontrollers usually have not.
|
|
|
|
|
You called this thread but you describe these as tasks and I read your other responses ... so I am assuming this is a task switcher.
So I am guessing you are on a pre-emtive task switcher that is running multiple tasks.
If that is the case usually you synchronize by stopping the task switcher for switching for a short period as you have a critical section.
For example of a context switcher running off a timer interrupt, you just disable the interrupts which will stop the context switcher from switching your task out. On all context switchers there is a way to do this because at various points in the kernel they have to enter critical sections. You need to keep your time inside these sections as fast as you possibly can.
Now if it is going to take to long you need to use locks. They start simple and grow in complexity as you want different things.
Even on a basic task switcher it should have a semaphore because they are so easy to define or you can make one just using a volatile int.
typepdef volatile int semaphore;
The volatile is important because you are going to end up looping around reading the same address. You need to make sure the compiler doesn't optimize that to only reading it once and using the old value for each of the check loops. It must read the memory location each and every time.
To enter a semaphore locked area generally you will make a call to something called a name like semaphore_inc or semaphore_pend.
What that will do is make sure you have single aquire on the semaphore, if you don't it will sleep your task it will basically be doing this
void semaphore_inc (semaphore* sem)
{
disable context switcher;
while (*sem != 0)
{
enable context switcher;
sleep(1);
disable context switcher;
}
*sem++;
enable context switcher;
}
The disable and enable context switcher will be simple disable and enable interrupt opcodes on a simple timer interrupt system up to a small piece of code on more advanced switchers. What is important is do you get what happens with your task getting sleeped unless it has solo access to the semaphore. The 1 will be the minimum unit of time on the task switcher until your task can be woken up. It's non blocking because the sleep call will activate the next task to run (because you can't and so you give up your timeslice) and it will come back to you sometime later on another context switch. The moment you set the semaphore any other task trying to enter would have the same happen to it because they would ask for the aquire in the same way. You can see it is important the context switcher get turned off while various parts of the test are done, you don't want to be pre-emptively switched out while you are testing.
Now once you have finished you call something like semaphore_dec, or semaphore_release which will set the semaphore back to zero again turning off the context switcher where it needs. Once you exit other tasks may enter or they will wake from a sleep and find they can go in if they were trying to aquire lock. If you have to write the code for the release it is a really simple code
void semaphore_dec (semaphore* sem)
{
disable context switcher;
*sem--;
enable context switcher;
}
So it ends up like this taskACall and taskBCall can never run at same time but they are non blocking. A task waiting to aquire the lock gives up it's timeslice to other tasks which will include the task currently in the locked area. Eventually the task in the locked area will come out and the waiting task will be able to enter but will lock the original task from re-entering until it leaves.
semaphore sem = 0;
void taskACall (void)
{
semaphore_inc(&sem);
.... // do task A code
semaphore_dec(&sem);
}
void taskBCall (void)
{
semaphore_inc(&sem);
.... // do task B code
semaphore_dec(&sem);
}
Now some CPU's and some systems have semaphores inbuilt into the MMU and C11 added atomics as a standard #include <stdatomic.h>.
So on different systems if you have to add the behaviour yourself there is a standard way to do it.
Without knowing more specifics that is about all I can tell you.
In vino veritas
modified 11-Sep-18 5:03am.
|
|
|
|
|
Thank you very much for the answer.
But the C program is in an embedded device, it's not a pre-emptive task environment, just a forever loop cycle, in which, some functions be called in 10 ms cycle, some functions be called in 5 ms cycle.
each cycle I called it a task.
The structure like a simple scheduler using while(1) loop.
Task A (or Cycle A) is a 10 ms cycle, and Task B is a 5 ms cycle, just use a counter to implemented the 10 ms , 5 ms cycle, like
while(1)
{
count ++;
if (count == 5)
{
task B be called
}
if (count == 10)
{
count = 0;
task A be called.
}
}
Thanks
|
|
|
|
|
 It's exactly the same you have to be manually yielding or forcibly switching or else you would never return.
So the semaphore code is exactly the same except you can ignore all the switcher enable and disable all you do is if the semaphore is locked you immediately switch to the other code (which is your version of sleep) because you can't do anything on that task.
So basically if taskA has the semaphore, then whenever you call taskB code and you want to enter the semaphore lock area you just exit and call taskA because taskB can't run until taskA gets it's fat butt out of the lock area. So in your case you would actually get a slight processing boost on taskA because it would get called twice (minus the exit and switch time) for each count loop.
The same applies if taskB has the semaphore and taskA went to enter the semaphore lock area then it would simply exit and call taskA.
It's pretty straight forward the task that is waiting on the lock is dead in the water and you just need it to give all the processing power to the other task to get it out of the lock area.
As your setup is so simple you could also just setup a state machine to mirror a semaphore, you have 5 states
1.) Neither A,B in lock area, 2.) A in lock area B out, 3.) A in B waiting, 4,) B in A out, 5.) B in A waiting
The states are changed as A & B enter and leave the lock area ...see functions in code after the modified work loop
enum lock_state {ABoutofLockArea = 0, AinLockAreaBnormal, AinLockAreaBwaiting, BinLockAreaAnormal, BinLockAreaAwaiting};
enum lock_state state = 0;
/* MODIFIED WORK LOOP */
while (1){
if (count == 5)
{
if (state == AinLockAreaBwaiting) TaskA is called // no point calling taskB its waiting
else task B be called
}
if (count == 10)
{
count = 0;
if (state == BinLockAreaAwaiting) TaskB is called // no point calling taskA its waiting
else task A be called.
}
}
/* HERE ARE YOUR STATE CHANGE FUNCTIONS */
void taskA_EnterlockArea(void)
{
if (state == BinLockAreaAnormal) state = BinLockAreaAwaiting; // B already in there A must wait
else state = AinLockAreaBnormal // B is not there so A can enter
}
void taskA_LeavinglockArea(void)
{
if (state == AinLockAreaBwaiting) state = BinLockAreaAnormal; // B is waiting it can now enter, A goes back to normal
else state = ABoutofLockArea; // Otherwise no one is in the lock area
}
void taskB_EnterlockArea(void)
{
if (state == AinLockAreaBnormal) state = AinLockAreaBwaiting; // A already in there B must wait
else state = BinLockAreaAnormal // A is not there so B can enter
}
void taskB_LeavinglockArea(void)
{
if (state == BinLockAreaAwaiting) state = AinLockAreaBnormal; // A is waiting it can now enter, B will go back to normal
else state = ABoutofLockArea; // Otherwise no one is in the lock area
}
The advantage the semaphore has over the state machine, is you can add more and more semaphores with no complexity increase. As you try to lock more areas through a state machine it quickly becomes overly complex.
In vino veritas
modified 11-Sep-18 19:51pm.
|
|
|
|
|
In this case you don't need synchronization at all.
|
|
|
|
|
Thanks,
First I think I don't need a synchronization.
But when I code like:
if (rcvd == true)
{
read rcv_buffer
...
rcvd = false;
}
dataCB(int ch_id, char *buf, int len, char stat)
{
rcvd = true;
if((rcvd == false)
{
receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
}
}
The program behavior is not correct
|
|
|
|
|
It isn't at all surprising it misbehaves you have >>>> SHARED DATA <<<<
You guys seem to think having a co-operative task switcher somehow makes you
immune to shared data issues ... well your wrong as you found out
lets give you the situation .. it may not be your case but follow what happens
task b picks up the count of the recieve buffer .. then yields to task a
task a changes the count value of the recieve buffer then yields back to task b
task b now wrongly writes the old count value back and your program now crashes
So the variable "count" is being accessed by both tasks it is shared, it is that simple
The same will be true of other variables you are accessing with both taskA and taskB
and if you drag all the stuff accessed by both tasks to an area they are called the
shared data and they are the problem.
Now what creates the problem above is yielding between the two tasks, while one task
is using the shared data.
Now on the code above it looks like taskB probably never yields while it is inside dataCB.
That is because it's a function and I doubt you can organize the yield.
However on your taskA code above which looks like it in a flat loop you also can not yield
to taskB anywhere in it not even in the "..." section. It must start and finish that entire
code without ever yielding to taskB or your code will crash. The hint to why it would crash
is the variable rcvd which is probably a bool and it's written to by both tasks a big no no
or danger Will Robinson, as per the example I gave at top.
If you understoof the issue you can refactor taskA code to make the problem less likely but
curing it if you want to yield in "..." needs definitive steps.
anyhow I will leave you with it.
In vino veritas
modified 13-Sep-18 1:03am.
|
|
|
|
|
If your system behaves as you describe, this is not synchronization problem. Task A and B are actually not tasks, these are synchronous functions. No parallel execution, every function has exclusive access to all variables (resources). Specifically, in your last code fragment:
rcvd = true;
if((rcvd == false)
{
receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
}
|
|
|
|
|
Since the tasks are not waiting on any object or variable, there will be no dead lock.
I guess you do need to add if (rcvd == false) in task B before the line rcvd = true;
«_Superman_»
I love work. It gives me something to do between weekends.
Microsoft MVP (Visual C++) (October 2009 - September 2013) Polymorphism in C
|
|
|
|
|
I know how to break execution when the memory at the specified address changes.
------------------------------
Menu->Debug->Windows->Breakpoints: New: New Data Breakpoints
------------------------------
But, I want to break execution when the memory at the specified address reads.
For example
int g_iNum = 123;
void Fun1()
{
int iTemp;
iTemp = g_iNum;
}
|
|
|
|
|
Perhaps, something like Function Breakpoint?
|
|
|
|
|
Use Windbg "ba r1 0000ffff" can break when address(0x0000ffff) reads.
But I don't know how to find where of code.
|
|
|
|
|
You have supplied no way to answer your question as you have not told us what system you are talking about.
Hence I can only tell you it is called a watchpoint
watchpoint - Wiktionary[^]
This is what you are wanting isn't it ===> "A debugging mechanism whereby execution is suspended every time a specified memory location is modified; or, any of various similar such mechanisms."
No way to answer you any deeper than that.
In vino veritas
|
|
|
|
|
Windows10, Visual Studio, C++
----------------------------
Use Windbg "ba r1 0000ffff" can break when address(0x0000ffff) reads.
But I don't know how to localition the source code.
|
|
|
|
|
|
Use Windbg "ba r1 0000ffff" can break when address(0x0000ffff) reads.
But I don't know how to open and location the source code.
|
|
|
|
|
The first thing that came to mind is to search for g_iNum in the entire code base and put breakpoints where ever it is being accessed.
«_Superman_»
I love work. It gives me something to do between weekends.
Microsoft MVP (Visual C++) (October 2009 - September 2013) Polymorphism in C
|
|
|
|
|
This MSDN page lists size as one of the primary components of a font. Do I understand correctly that this means Times New Roman is a typeface but not a font, 8pt Times New Roman is a font, and 12pt Times New Roman is a different font? If so, it seems like the word font is used in places I would intuitively have thought deal with typefaces. For example, this talks of installing a "font." I thought the way fonts work is that there is a file that specifies the shape of each character, which can then be drawn at whatever scale one desires. In other words, the thing one installs is Times New Roman, not Times New Roman at a specific point size. Is that right?
I have more questions, which deal directly with WinAPI, but they hinge on the answer to this one.
|
|
|
|
|
This is no standard use of what the word font means, it means different things to different people. Typeface is a term with far more precision because it comes from the print industry and the general public would rarely use the word.
That said, Windows deals with two main classes of fonts by which they really mean file types. They have bitmap fonts (sometimes called screen fonts or raster fonts) and they have scaleable fonts (sometimes called vector fonts).
So specifically when you are installing a font either of your statements could be true. If you are installing a raster/bitmap font file then you will get the size it represents as pixels in the file and optionally a limited number of scaled sizes. If you are installing a truetype, adobe or postscript vector font you get full the typeface and the ability to scale any size you like.
To make matters more confusing Windows carries a system called cleartype which may or may not be in use by a program. Cleartype is a subpixel render that when you re-scale a font and it is converted to raster (bitmap) for the final display, it makes decisions about those pixels on the boundary that are partially in and out. It doesn't make the crass blank decision that if 50% or more is inside then it turns the pixel on. It uses shading and hints built into the font to make the decision instead.
Then finally in windows there is a further complication that all of what we have discussed is meaningless without knowing which device context we are talking about. A screen device context is very different to a printer device context, as an example there is no cleartype on any printer device driver I know of. Some printer drivers may not even support every font size and type and they are allowed to silently pull a font substitution. So anything we discuss may be wrong on a different device context.
A lot of this is historic and even now largely unavoidable Windows for device drivers is not a closed system. Any manufacturer or developer can write and install a device driver. Linux is even more uncontrolled in many ways because the O/S sourecode can be changed by anyone and there are even more options and differences there.
At the moment you haven't dealt with one of the huge variations which is what characters are represented in the font or in your terms you would probably say how many glyphs are represented in the typeface. This leads into what keys maps to what glyphs which is called mapping or encoding by the O/S. Even the display direction is not set in stone unlike English many languages go right to left and it is supported. Being able to type vertically may or may not be supported the usual problem it throws up is condensing and proportionality rules get thrown into chaos.
If you want more precision around things you are going to also give us precision that is what device context are we talking about when you ask the question. Device context here as a very precise meaning on windows
About Device Contexts | Microsoft Docs[^]
In a programming sense to do anything with a font, you first create a device context and every operation on a font requires you to send the device context as part of that call. So I can't answer much without knowing what device context you are talking about.
In vino veritas
modified 6-Sep-18 23:24pm.
|
|
|
|
|
This does clarify some of the questions I would otherwise have asked already. With that, I'll go into the specific scenario I'm dealing with: I'm working on a music notation program, which will be getting its music symbols from a font. (For now it will involve drawing symbols to the screen, so looks like that makes display device contexts the relevant ones.) I have installed the Bravura font, and would like to use it for this purpose. The first difficulty is the proper way to select it. My understanding is that what I installed is an example of what MSDN calls a physical font, and there is no way to select a physical font directly, all font selection instead involving specifying a set of properties one wants the font to have, then allowing Windows to find the physical font that most closely matches those properties from among those available to the device in question. Is that right?
I had no idea what values I would need to specify for most of the parameters of CreateFont in order for the font mapper to find Bravura as a match. The cHeight and cWidth parameters are especially confusing for reasons I'll get back to, but many of the other parameters are too, given that they seem to be text-specific, while my font isn't used for text. So instead I called EnumFonts. I assume it's possible for this function to find more than one match for the given typeface name, but am unsure about the implications of the number of matches it finds. I believe my font should be one that can be scaled freely to different sizes. Would one expect EnumFonts to call the callback specified by the lpProc parameter once for every possible size such a font can be rendered in? Or, if it calls the callback only once (which is what happened for me), what do the lfHeight and lfWidth fields of the LOGFONT struct passed to it indicate, if not that the font can only be rendered at that one (logical-unit) size after all?
I have still more questions after this, but it will be easier to check whether I'm in the right ballpark with what I've said so far first.
modified 7-Sep-18 0:32am.
|
|
|
|
|