Tag Archive : programming

/ programming

C++11 introduces several new type inference capabilities. It mean you can spend less time having to write out things the compiler already knows. Of course there are times when you need to help out the compiler or your fellow programmers. But with C++11, you can spend less time on the mundane stuff and focus on the logic.

Let’s start by looking at the most immediately obvious new benefit, the auto keyword

The joy of auto

In C++11, the compiler can infer the type of a variable at the point of declaration. So, instead of putting in the variable type, you can just write auto:

long x = 4L;

can now be replaced with

auto x = 4L;

This is look simple. But the most benefit would be when we use templates and iterators:

vector<int> vec;
auto itr = vec.iterator(); // instead of vector<int>::iterator itr

There are other times where auto comes in handy, too. For example, let’s say that you had some code of the form:

template <typename BuiltType, typename Builder>
void
makeAndProcessObject (const Builder& builder)
{
    BuiltType val = builder.makeObject();
    // do stuff with val
}

In this code, you can see that there are two necessary template parameters–one for the type of the “builder” object, and a second for the type of the object being built. Even worse, the type of the built object cannot be deduced by the template parameter. Every call must look like this:

MyObjBuilder builder;
makeAndProcessObject<MyObj>( builder );

But auto immediately reduces this ugliness to nothing because we no longer need to be able to write the specific type at the point where you build the object. We can let C++ deduce it for us:

template <typename Builder>
void
makeAndProcessObject (const Builder& builder)
{
    auto val = builder.makeObject();
    // do stuff with val
}

Now we only need a single template parameter, and that parameter is easily inferred when calling the function:

MyObjBuilder builder;
makeAndProcessObject( builder );

This is way better for the caller, and the template code loses nothing in readability–if anything, it’s easier to understand!

Decltype and the new return value syntax

Now you might be saying here — okay, that’s great, but what if I wanted to *return* the value that this builder object created? I still need to provide the template argument becuase I need to provide the return type. Well, it turns out that the standards committee is full of smart people, and they have an extremely nice solution to this problem. It comes in two parts: decltype and the new return value syntax.

New Return Value Syntax

Let’s start off with the new, optional, return value syntax, since it manages to find yet another use for auto. In all prior versions of C and C++, the return value of a function absolutely had to go before the function:

int multiply (int x, int y);

In C++11, we can now put the return value at the end of the function declaration, substituting auto for the name of the return type, if we want to:
auto multiply (int x, int y) -> int;
So would you want to do this? Let’s look at a simple example where it helps us: a class with an enum declared inside it:

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

Here we have a simple class, Person, that has a type: whether the person is an adult, a child, or a senior citizen. Not much special about it, but what happens when you go to define the methods?

The first method, the setter, is trivial to declare, you can use the enum type PersonType without any trouble:

void Person::setPersonType (PersonType person_type)
{
    _person_type = person_type;
}

On the other hand, the second method is a bit of a mess. The nice clean looking code won’t compile:

// the compiler doesn't know what PersonType is because PersonType 
// is being used outside of the Person class
PersonType Person::getPersonType ()
{
    return _person_type;
}

We have to write:

Person::PersonType Person::getPersonType ()
{
    return _person_type;
}

to make the return value work correctly. This isn’t that big of a deal, but it’s pretty easy to do by mistake, and it can get much messier when templates are involved.

This is where the new return value syntax comes in. Because the return value goes at the end of the function, instead of before it, you don’t need to add the class scope. By the point the compiler reaches the return value, it already knows the function is part of the Person class, so it knows what PersonType is.

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

Okay, while that’s very nice, does it really help us out? We can’t use this new syntax to solve the problem we had before, can we? Well, not yet. Let’s add one more concept: decltype

decltype

Decltype is auto’s not-evil twin. Auto lets you declare a variable with a particular type; decltype lets you extract the type from a variable (or any other expression).

int x = 3;
decltype(x) y = x; // same thing as auto y = x;

You can use decltype for pretty much any expression, including to express the type for a return value from a method. Hmm, that sounds like a familiar problem doesn’t it? What if we could write:

decltype( builder.makeObject() )

This would give us the type that is returned from makeObject, allowing us to specify the return value from makeAndProcessObject. We can combine this with the new return value syntax to produce this method:

template <typename Builder>
auto
makeAndProcessObject (const Builder& builder) -> decltype( builder.makeObject() )
{
    auto val = builder.makeObject();
    // do stuff with val
    return val;
}

This only works with the new return value syntax because under the old syntax, we couldn’t refer to the function argument, builder, at the point where we declare the return type. With the new syntax, all of the arguments to a function are fair game!

Auto, References, Pointers and Const

One question that is sure to come up is how auto handles references:

int& foo();

auto bar = foo(); // int& or int?

The short answer is in C++11, auto defaults to being by-value for references, so in the above code bar is an int. However, you can add the & as a modifier to force it to be a reference:

int& foo();

auto bar = foo(); // int
auto& baz = foo(); // int&

On the other hand, if you have a pointer auto will automatically pick up pointerness:

int* foo();

auto p_bar = foo(); // int*

But we can also (thankfully) be explicit about it, and indicate that the variable is a pointer:

int* foo();
auto *p_baz = foo(); // int*

You can similarly tack const onto auto if you need it, when dealing with references:
int& foo();

const auto& baz = foo(); // const int&

Or with pointers:

int* foo();
const int* const_foo();
const auto* p_bar = foo();   // const int*
auto p_bar = const_foo();    // const int*

Overall, it feels quite natural and normal, and it follows the normal type inference rules of templates in C++.

As this article written, GCC 4.4 and MSVC 10 both support everything we have discussed about in this article. To use C++11 feature on gcc, you can add -std=c++0x on your compiling command. Try to use GCC or using MSVC 10, and start using these techniques today. If you’re using another compiler, check out their page for details of C++11 compiler support.

References: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html

In previous article, we have discussed some basic for network programming such as IP, port number, and byte order of computer. In this article we will look some API and its basic usage.

Converting Byte Order

We have know that each computer might have different endian but the network infrastructure ensure format in network order. To convert our host order to network order (or vice versa) we can use some function:

htons()
htonl()
ntohs()
ntohl()

Some Structs

We will discussed some struct used here. Try to remember them, okay?

struct addrinfo – This structure is more recent invention and is used to prepare the socket address structures for subsequent use. It’s also used in host name lookups, and service name lookups.

struct addrinfo {
   int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc
   int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC
   int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM
   int              ai_protocol   // use 0 for "any"
   size_t           ai_addrlen;   // size of ai_addr in bytes
   struct sockaddr *ai_addr;      // can be struct sockaddr_in or sockaddr_in6
   char            *ai_canonname; // full canonical hostname
   struct addrinfo *ai_next;	  // linked list, next node
};

Load this struct up a bit and then call getaddrinfo(). It’ll return a pointer to a new linked list of these structures filled out with all the information needed. We can force it to use IPv4 or IPv6 or leave it as AF_UNSPEC to use whatever.

struct sockaddr – This structure hold information about address.

struct sockaddr {
   unsigned short  sa_family;    // address family, AF_XXX
   char            sa_data[14];  // 14 bytes of protocol address;
};

sa_family can be variety, but it’ll be AF_INET or AF_INET6 (you must have known the difference when I write that ;p . sa_data containes destination address and port number for socket.

struct sockaddr_in – structure for specific use, IPv4. Make reference elements of the socket address. Please remember that sin_zero must be set to all zero with memset() function.

struct sockaddr_in {
   short int          sin_family;    // Address family, AF_INET
   unsigned shot int  sin_port;      // Port number
   struct in_addr     sin_addr;      // Internet address
   unsigned char      sin_zero[8];   // filled with zero to keep same size as sockaddr
};

struct in_addr – structure for hold internet address (IPv4)

struct in_addr {
   uint32_t s_addr;    // 32 bit int
};

struct sockaddr_in6 – structure for specific use, IPv6. Similiar to IPv4 version

struct sockaddr_in6 {
   u_int16_t        sin6_family;    // Address family, AF_INET6
   u_int16_t        sin6_port;	    // Port number, network byte
   u_int16_t        sin6_flowinfo;  // IPv6 flow information
   struct in6_addr  sin6_addr;      // IPv6 address
   u_int32_t        sin6_scope_id;  // Scope ID
};

struct in6_addr – structure for hold internet address (IPv6)

struct in6_addr {
   unsigned char s6_addr[16];   // IPv6 address
};

struct sockaddr_storage – structure large enough to hold both IPv4 and IPv6.

struct sockaddr_storage {
   sa_family_t  ss_family;	// address family

   // all this is padding, implementation specific
   char         __ss_pad1[_SS_PAD1SIZE];
   int64_t      __ss_align;
   char         __ss_pad2[_SS_PAD2SIZE];
};

The important one is the ss_family field, we can check it for AF_INET or AF_INET6. Then we can cast it to struct sockaddr_in or struct sockaddr_in6 if we want.

IP Address Representation

There are a bunch of function to manipulate IP address. If we have IP address “10.12.110.57” and “2001:db8:63b3:1::3490” we will use inet_pton() function to converts them to their respective address representation. To reverse it (convert to internet address representation to string), use inet_ntop() function.

#include <arpa/inet.h>

struct sockaddr_in sa;	// IPv4
struct sockaddr_in6 sa6	// IPv6
char ip4[INET_ADDRSTRLEN];
char ip6[INET6_ADDRSTRLEN];

inet_pton(AF_INET,"10.12.110.57",&(sa.sin_addr));	// IPv4
inet_pton(AF_INET6,"2001:db8:63b3:1::3490", &(sa6.sin6_addr));	// IPv6

inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("IPv4 Address is: %s\n", ip4);
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("IPv6 Address is: %s\n", ip6);

Socket Descriptor

As we discussed in some articles ago, a socket in Unix is a file associated with a integer. To create a socket we need create a variable for socket which can be as simple as integer.

Socket Creation

To create a socket we will call a function socket(). Function socket() has 3 arguments: a family type (AF_INET / AF_INET6), packet type (SOCK_STREAM / SOCK_DGRAM), protocol type.

#include <sys/types.h>
#include <sys/socket.h>

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);

Socket Bind to

Socket has been created on creation step and stored in socket_fd. But it would be meaningless if we cannot use it for communication because it has not bind into any port. Bind a port mean attach our socket to port and create a “tunnel” for our application to the port, exclusively. Once the socket bounded to a port, other program cannot use same port until our socket is unattached from the port. The information needed would be stored in struct sockaddr_* respective to type of address it want to use (IPv4 or IPv6).

#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr_in sin;

memset(sin.sin_zero,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(13510);
inet_pton(AF_INET, "127.0.0.1", &(sa.sin_addr));

bind( socket_fd, (struct sockaddr*) &sin, sizeof(sin) );

Socket Listen to

The bounded socket can listen to some incoming connections. A function listen() will set the maximum number of connection it can handle.

#include <sys/types.h>
#include <sys/socket.h>

listen( socket_fd, 20 );

Socket Accept request

Once a listening socket got an incoming packet, it is up to programmer to accept it or drop it. To accept incoming packet we use accept function and create a new file descriptor. The data can be fetched from new file descriptor (remember that everything in Unix is file?). Peer is remote computer who initiate communication.

#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr* peer_addr;

int accfd = accept( socket_fd, (struct sockaddr*) peer_addr, sizeof(peer_addr) );

Socket Receive message

After accepting connection, we have a new file descriptor that store message. The next thing to do is fetching the data.

#include <sys/types.h>
#include <sys/socket.h>

char message[1024];
int nbytes;

nbytes = recv(accfd, message, 1024, 0);

Socket Connect to

A socket can also used for sending message or simply connecting to another host. The open socket, socket created from socket(), is unnecessary to bounded to any port. It only need information about address it want to connect.

#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>

struct sockaddr_in sin;

memset( &sin.sin_zero, 0, sizeof(sin) );
sin.sin_family = AF_INET;
sin.sin_port = htons(13510);
inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr));

int result = connect( socket_fd, (struct sockaddr*) &addr, sizeof(addr) );

Socket Send message

When connection established, we can send message over our socket.

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>

char message[1024];
strcpy(message,"To server, hello");
send(sock_fd, message,1024,0)

Last article, we have discussed about what is socket. This article will give a quick presentation about network before start learning socket programming.

Internet Protocol (IP) Address and Subnetwork

Internet Protocol (IP) Address or IP Address is simply an address. Like address in real world, IP address determine how your device can be reach from other device. IP address set a complex message transferring system in internet which is called routing, and responsible to route data packets from one end to another end.

There are 2 type of IP address used in public: IPv4 (IP version 4) and IPv6 (IP version 6).

IPv4 has 32 bit length with representation series of decimal numbers separated by dot. For example 192.168.1.1 is IPv4 address. With IPv4 there would be 2^32 addresses or 4,294,967,296 (approximately 4 billion address). These addresses are currently running out so an alternative address named IPv6 is developed.

IPv6 has 128 bit length with representation series of hexadecimal numbers separated by colon. For example 2001:0db8:c9d2:0012:0000:0000:0000:0051. With IPv6 there would be 2^128 addresses for entire planet. This is relatively huge, far more huge than older IPv4 way.

There is an organization Internet Assigned Number Authority (IANA, http://www.iana.org/) that manages global IP allocation and delegates it to five Regional Internet Registries (RIRs) to allocate IP block to Local Internet Registries (LIRs) and other organization. So RIR are IANA’s delegation to manages all IP address around the world. Those 5 regions are:

  1. African Network Information Center (AfriNIC) for Afrika.
  2. American Registry for Internet Numbers (ARIN) for United States, Canada, and some region of Caribbean.
  3. Asia-Pacific Network Information Center (APNIC) for Asia, Australia, New Zealand, and surrounding nation.
  4.  Latin America and Caribbean Network Information Center (LACNIC) for Latin America and some region of Caribbean.
  5. Réseaux IP Européens Network Coordination Centre (RIPE) for Europe, Middle East, and Middle Asia.

While LIRs are organization in every nation. They allocate IP address for customer in each nation but still under supervisor of their own RIR.

Subnetwork or subnet is a logically visible subdivision of an IP network. All computers that belong to a subnet are addressed with a common, identical, most-significant bit-group in their IP address. This results in the logical diviion of an IP address into two fiels: a network or routing prefix and the rest field or host identifier. The rest field is identifier for a specific host or network interface.

The routing prefix is expressed in CIDR notation and written as the first address of a network followed by a slash character (/), and ending with the bit-length of prefix. For example 202.168.1.0/24 is the prefix of the IPv4. The example for IPv6 is 2001:db8::/32 is a large network with 2^96 address, having a 32-bit routing prefix. In IPv4 the routing prefix is also specified in the form of the subnet mask, which is expressed in quad-dotted decimal representation like an address, for example: 255.255.255.0 is network mask for 24-bit routing prefix.

Port Numbers

In computer network, a port is a logical gateway to exchange information to the world. Like a port in real world, incoming and outgoing packet are visiting port each time. Therefore it is a vital part for communication.

Port Number is a number that associated with kernel. A port number is 16-bit number that is like local address for the connection.

We can associate IP address like address of a building (apartment), while port number is room number for each attendant (a program). A message passed from one program to another program can be in same building (same machine) or different building (different machine) via postal agent (internet).

Port number is also differentiate services on a server, such as mail server and web server.

Different services on internet have different well-known post numbers. There is a big list maintained by IANA or if you are on Unix machine you can see the list on /etc/services file. HTTP (web) is port 80, telnet is 23, SMTP is 25, and so on.

Byte Order

There are two way computer might store number, in Big-Endian or Little-Endian. A Big-Endian system store number with big end first. While Little-Endian is the reverse. Intel and Intel-compatible processor store data in Little-Endian. Therefore, a number in hexadecimal 0xb34f would be stored in memory as sequential 4f followed by b3. In Big-Endian, 0xb34f would be stored in memory in two sequential bytes b3 followed by 4f.

The computer dependent number presentation (can be Little-Endian or Big-Endian depend on processor) is called Host Byte Order. The data across network is standardized using Big-Endian for clarity. The byte order for network is  called Network Byte Order.

What is Socket?

November 24, 2015 | Article | No Comments

In the Computer Science world, especially in networking, socket play a great role. Socket is an interface for our computer programs in order to communicate each other in this vast world. Simply it is a way to “speak” using standard Unix file descriptor. I said standard unix descriptor and yes it is basically same like file handling like create, open, write, read but with some more capabilities.

“Everything in Unix is a file!”

You may have heard those statement, well at least you have read a minute ago in this article. And yes, everything in Unix is a file. Unix do any sort of I/O by reading or writing to a file descriptor. A file descriptor is simply an integer associated with an open file. A file is Unix can be anything. It can be real file on disk, a network connection, a FIFO, a pipe, a terminal, and so on. So when you want to connect to another program over Internet you will do it through a file descriptor.

There are many kinds of sockets. There are DARPA Internet address (Internet Sockets), path names on a local node (Unix Sockets), CCITT X.25 (X.25 Sockets), and probably many others depending on which Unix subsystem. The most common is Internet Sockets as it is used for internet nowaday.

There are two commons type of internet sockets: SOCK_STREAM (stream socket) and SOCK_DGRAM (datagram socket).The types refer to connection state of them. SOCK_STREAM Is connection oriented socket while SOCK_DGRAM is a connectionless socket. They are differ from the behaviour the transmission they have to do.

A connection oriented is like its name, intend to be reliable using connection. Every packet transmitted using this socket has to be acknowledged. A receiver must send a message saying “Hey, I have receive your message” to sender so that sender know that its message has arrived at receiver end. While in the connectionless stream, a sender just send the data without requiring acknowledged message from receiver.

A connection oriented is needed if you need transferred data arrived in exactly same order. It might be bad if you send a file and your data on 128th byte is swapped with 129th because of delay. The connection-oriented ensure packet to arrive in exact order with exact size.

A connectionless is needed if you need transferred real time data regardless of order. You might consider streaming is one of example. One or two packet arrive late (arrive out of order or maybe not arrive at all) is not a problem while you get real-time update of video (a football match maybe).

Unix and it’s successor have sockets. Windows too, in it’s own way, has socket. Every modern OS must have socket as a standard way to communicate.

Next I will show you how to socket-programming in general way.

A Little Advice in Making C Header File

October 19, 2015 | Article | No Comments

As we know, C and C++ programming language are supporting modular programming. It means that we can break our long code into several separate modules. For this we need a file named header file that mostly are set of function prototypes. Later the functions will be implemented on another source file. Now, what are things we should (or should not) put in header file? When we should create a header file?

This article will give little advice about what we should include in our header file. This list is based on personal experience, so you can agree or reject it.

[DO] Always Create “guard” Macro for Each Header

What is “guard” that I mean? They are 3 line of code that protect your header files from multiple inclusion. This is essentially important as we can include our header as many as we like but compiler only read it once. Therefore, we can guarantee that our declarations are only once. The “guard” are composed by macro #ifndef, #define, #endif. You must check a unique macro name that acts as you unique header identifier. If not exist, we declare our code. Otherwise, the compiler will ignore the declaration. Our code will be between #define and #endif code respectively.

[DO] Create One Header File for Each “Module” of the System

Yes, for each. A module may comprise one or more compilation units. But it should implement just one aspect of the system. Examples of well-chosen modules are:

  • device driver for A/D converter
  • communication protocol
  • alarm manager that is responsible for logging error conditions and alerting the user.

Every module must be create on one header file.

[DO] Include All of the Function Prototypes for Public Interface of Module

For example, a header file lcd.h might contain function prototype for lcd_init(), lcd_write(), and lcd_clear(). But private implementation or helper functions that only known or used by those functions must be remain hidden.

[DON’T] Include Any executable Lines of Code in a Header file, Including Variable Declarations.

It’s not so effective. Another reason is there would be a chance of fatal error by multiple declaration. Especially if you are not providing “guard” for header files. But it is necessary to make an exception for the bodies of some inline functions.

[DON’T] Expose Any Variable in Header File

Again! If you want to make a variable public or known by another module, it’s not wise to put them on header file. Use extern in header file while the actual declaration would be in source code file (.c file).

[DON’T] Expose Internal Format of Any Module-Specific Data Structure Passed to or Returned from One or More of the Module’s Interface Functions

No declaration of structure in header file. Implicitly, no “struct { … } foo;” like code in any header file. If you do have a type that you need to pass in and out of your module so client can create instance of it, simply use “typedef struct moduleb_type” in header type. Outside that module should never know the internal format of the struct.

Maybe this is strict, but I hope this advice will give a better and readable C programming practice. More advice would be added later.

C volatile keywords

October 19, 2015 | Article | No Comments

In C/C++ programming language, volatile keyword is a type qualifier used to declare an object that can be modified in the program by something such as operating system, the hardware, or a concurrently executing thread.

In reality, the C’s volatile keyword is poorly understood by many programmers and used not properly. It’s not surprising as most book covering C language only explain it in one or two sentence. This article will covering about a proper way to use volatile. This article is categorized as little advanced so I hope you have understand C\C++.

Mostly, people experience these while programming C\C++ for embedded code (check for yourself):

  • Code works fine until compiler optimizations enabled
  • Code works fine until interrupts enabled
  • Flaky hardware drivers
  • RTOS tasks that work fine in isolation until other task is spawned.

If you ever experience any of above, most likely you didn’t use the C keyword volatile.

C’s volatile keyword is a qualifier that is applied to a variable when declared and tells the compiler that the value of the variable may change at any time without any action being taken by the code that compiler finds nearby. This is quite serious.

There are some declaration that may have different meaning. Let’s inspect further.

To declare a variable volatile, include the keyword volatile before or after the data type in variable declaration. Both have same meaning:

volatile int xathrya;
int volatile xathrya;

Those are very common declaration. Now, there is another way of declaring pointer. Both of these declaration will declare a pointer to a volatile unsigned integer:

volatile unsigned int * xathrya;
unsigned int * xathrya;

Next, we can have volatile pointers to non-volatile data. But this is very uncommon:

int* volatile xathrya;

You may think that we can have a volatile pointer to volatile variable, and yes this is possible:

int volatile * volatile xathrya;

If you apply volatile to a struct, union, or a class, the entire contents of struct/union/class would be volatile too. If you don’t want this, apply volatile to individual members of struct/union/class.

Now speaking about proper use of volatile. When we declare volatile? Whenever a variable’s value could change unexpectedly. In practice there are only three types of variable could changes:

  1. Memory-mapped peripheral registers
  2. Global variables modified by an interrupt service routine
  3. Global variables accessed by multiple tasks within a multi-threaded application

Let’s discuss each case!

Peripheral registers

Embedded systems contain real hardware, usually with sophisticated peripherals. These peripherals contain registers whose values may change asynchronously to the program flow. As a very simple example, consider an 8-bit status register that is memory mapped at address 0x1234. It is required that you poll the status register until it becomes non-zero. The naive and incorrect implementation is as follows:

uint8_t* pReg = (uint8t *) 0x1234;
// wait for register to become non-zero

while(*pReg == 0) {  } // do something else

This will almost certainly fail as soon as you turn compiler optimization on, since the compiler will generate assembly language that looks something like this:

mov ptr, 0x1234
mov a, ptr
loop:
bz loop

The rationale of the optimizer is quite simple: having already read the variable’s value into the accumulator (on the second line of assembly), there is no need to reread it, since the value will always be the same. Thus, in the third line, we end up with an infinite loop. To force the compiler to do what we want, we modify the declaration to:

uint8_t volatile* pReg = (uint8_t volatile *) 0x1234;

Thus the assembly language would looks like:

mov ptr, 0x1234
loop:
mov a, ptr
bz loop

The desired behavior is achieved.

Subtler problems tend to arise with registers that have special properties. For instance, a lot of peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you are intending can cause quite unexpected results in these cases.

Interrupt Service Routines

Interrupt service routines often set variables that are tested in mainline code. For example, a serial port interrupt may test each received character to see if it is an ETX character (presumably signifying the end of a message). If the character is an ETX, the ISR might set a global flag. An incorrect implementation of this might be:

int etx_rcvd = FALSE;

void main() {
...

   while(!ext_rcvd) {

      // wait
   }

...

}

interrupt void rx_isr(void) {

...

   if(ETX == rx_char) {
      etx_rcvd = TRUE;
   }

...

}

With compiler optimization turned off, this code might work. However, any half decent optimizer will “break” the code. The problem is that the compiler has no idea that etx_rcvd can be changed within an ISR. As far as the compiler is concerned, the expression !ext_rcvd is always true, and, therefore, you can never exit the while loop. Consequently, all the code after the while loop may simply be removed by the optimizer. If you are lucky, your compiler will warn you about this. If you are unlucky (or you haven’t yet learned to take compiler warnings seriously), your code will fail miserably. Naturally, the blame will be placed on a “lousy optimizer.”.

The solution is to declare the variable etx_rcvd to be volatile. Then all of your problems (well, some of them anyway) will disappear.

Multi-threaded applications

Despite the presence of queues, pipes, and other scheduler-aware communications mechanisms in real-time operating systems, it is still fairly common for two tasks to exchange information via a shared memory location (that is, a global). Even as you add a preemptive scheduler to your code, your compiler has no idea what a context switch is or when one might occur. Thus, another task modifying a shared global is conceptually identical to the problem of interrupt service routines discussed previously. So all shared global variables should be declared volatile. For example, this is asking for trouble:

int cntr;

void task1(void) {
   cntr = 0;
   while(cntr == 0) {
      sleep(1);
   }

...

}

void task2(void) {

...

   cntr++;
   sleep(10);

...

}

With a simple observation, we will know that this code will likely fail once the compiler’s optimizer is enabled. Declaring cntr to be volatile is the proper way to solve it.

Some compilers allow you to implicitly declare all variable as volatile. But, resist this temptation since it is essentially a substitute for thought. It also leads to potentially less efficient code. Use volatile if you must.

Social media & sharing icons powered by UltimatelySocial