Sample code that allocates a named-memory
The sample code allocates a named-memory block named MyInfo_memory for the MyInfo structure. It then locks a critical section of code before updating the is_initialized integer in this named-memory block.
MyInfo *GetMyInfo()
{
mi_string *memname="MyInfo_memory",
msgbuf[80];
mi_integer status;
MyInfo *my_info = NULL;
/* Allocate the named-memory block. If it has already been
* allocated, obtain a pointer to this block.
*/
status = mi_named_zalloc(sizeof(MyInfo),
memname, PER_SESSION, (void **)&myinfo);
if( status == MI_NAME_ALREADY_EXISTS )
status = mi_named_get(memname, PER_SESSION,
(void **)&my_info);
switch(status)
{
case MI_ERROR:
mi_db_error_raise(NULL, MI_EXCEPTION,
"GetMyInfo: mi_named_get or mi_named_zalloc failed.");
return (MyInfo *)NULL;
break;
/* Have a pointer to the named_memory block. */
case MI_OK:
break;
case MI_NO_SUCH_NAME:
mi_db_error_raise(NULL, MI_EXCEPTION,
"GetMyInfo: no name after good get");
return (MyInfo *)NULL;
break;
default:
sprintf(msgbuf,
"GetMyInfo: mi_named memory case %d.", status);
mi_db_error_raise(NULL, MI_EXCEPTION, msgbuf);
return (MyInfo *)NULL;
break;
}
/*
* BEGIN CRITICAL SECTION.
*
* All access to the my_info structure is done
* inside this lock-protected section of code.
*
* If two threads try to initialize information
* at the same time, the second one blocks on
* the mi_lock_memory call.
*
* A reader also blocks so that it gets a
* consistent read if another thread is updating
* that memory.
*/
status = mi_lock_memory(memname, PER_SESSION);
switch(status)
{
case MI_ERROR:
mi_db_error_raise(NULL, MI_EXCEPTION,
"GetMyInfo: mi_lock_memory call failed.");
return (MyInfo *)NULL;
break;
case MI_OK:
break;
case MI_NO_SUCH_NAME:
mi_db_error_raise(NULL, MI_EXCEPTION,
"mi_lock_memory got MI_NO_SUCH_NAME.");
return (MyInfo *)NULL;
break;
default:
sprintf(msgbuf,
"GetMyInfo: mi_lock_memory case %d.",
status);
mi_db_error_raise(NULL, MI_EXCEPTION, msgbuf);
return (MyInfo *)NULL;
break;
}
/* The lock on the named-memory block has been
* obtained.
*/
/* The mi_named_zalloc() call above zeroed out
* the structure, like calloc(). So if the is_initialized
* flag is set to zero, named memory has not been
* initialized yet.
*/
if (my_info->is_initialized == 0)
{
/* In this block we populate the named-memory
* structure. After initialization succeeds, set the
* is_initialized flag.
*
* If any operation fails, MUST release the lock
* before calling mi_db_error_raise():
*
* if (whatever != MI_OK)
* {
* mi_unlock_memory(memname, PER_SESSION);
* mi_db_error_raise(NULL, MI_EXCEPTION,
* "operation X failed!");
* return (MyInfo *)NULL;
* }
*
*/
my_info->is_initialized = 1;
} /* endif: MyInfo structure not initialized */
else
{
/* Update or get a consistent read here. Again,
* before any exception is raised with
* mi_db_error_raise(), the lock MUST be released.
*/
}
/*
* END CRITICAL SECTION.
*/
mi_unlock_memory (memname, PER_SESSION);
return my_info;
}
The preceding code fragment uses the mi_lock_memory() function
to obtain the lock on the named memory. The following code fragment
uses mi_try_lock_memory() to try to get a lock
on a named-memory block 10 times before it gives up:
for ( lockstat=MI_LOCK_IS_BUSY, i=0;
lockstat == MI_LOCK_IS_BUSY && i < 10;
i++ )
{
lockstat = mi_try_lock_memory(mem_name, PER_STMT_EXEC);
switch( lockstat )
{
case MI_OK:
break;
case MI_LOCK_IS_BUSY:
mi_yield(); /* Yield the processor. */
break;
case MI_NO_SUCH_NAME:
mi_db_error_raise(NULL, MI_EXCEPTION,
"Invalid name of memory after good get");
return MI_ERROR;
break;
case MI_ERROR:
mi_db_error_raise(NULL, MI_EXCEPTION,
"Lock request failed.");
return MI_ERROR;
break;
default:
mi_db_error_raise(NULL, MI_EXCEPTION,
"Invalid status from mi_try_lock_memory()");
return MI_ERROR;
break;
}
}
/* Check the status after coming out of the loop. */
if( lockstat == MI_LOCK_IS_BUSY )
{
mi_db_error_raise(NULL, MI_EXCEPTION,
"Could not get lock on named memory.");
return MI_ERROR;
}
/* Now have a locked named-memory block. Can perform a
* read or update on the memory.
*/
...
mi_unlock_memory(mem_name, PER_STMT_EXEC);
Usually, the mi_try_lock_memory() function is a better choice than mi_lock_memory() for lock requests because mi_try_lock_memory() does not hang if the lock is busy.
The database server does not release any locks you acquire on named
memory. You must ensure that your code uses the mi_unlock_memory() function
to release locks in the following cases:
- Immediately after you are done accessing the named memory
- Before you raise an exception with mi_db_error_raise()
- Before you call another DataBlade API function that raises an exception internally (For more information, see Handling errors from DataBlade API functions.)
- Before the session ends
- Before the memory duration of the named memory expires
- Before you attempt to free the named memory
Important: After you obtain a lock
on a named-memory block, you must explicitly release it with the mi_unlock_memory() function. Failure to release
a lock before one of the previous conditions occurs can severely impact
the operation of the database server.