How to recreate swapchain after vkAcquireNextImageKHR is VK_SUBOPTIMAL_KHR? - semaphore

This vulkan tutorial discusses swapchain recreation:
You could also decide to [recreate the swapchain] that if the swap chain is suboptimal, but I've chosen to proceed anyway in that case because we've already acquired an image.
My question is: how would one recreate the swapchain and not proceed in this case of VK_SUBOPTIMAL_KHR?
To see what I mean, let's look at the tutorial's render function:
void drawFrame() {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapChain();
return;
/* else if (result == VK_SUBOPTIMAL_KHR) { createSwapchain(); ??? } */
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("failed to acquire swap chain image!");
}
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]);
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = {swapChain};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
The trouble is as follows:
vkAcquireImageKHR succeeds, signaling the semaphore and returning a valid, suboptimal image
Recreate the swapchain
We can't present the image from 1 with the swapchain from 2 due to VUID-VkPresentInfoKHR-pImageIndices-01430. We need to call vkAcquireImageKHR again to get a new image.
When we call vkAcquireImageKHR again, the semaphore is in the signaled state which is not allowed (VUID-vkAcquireNextImageKHR-semaphore-01286), we need to 'unsignal' it.
Is the best solution here to destroy and recreate the semaphore?

Ad 3: you can use the old images (and swapchain) if you properly use the oldSwapchain parameter when creating the new swapchain. Which is what I assume the tutorial suggests.
Anyway. What I do is that I paranoidly sanitize that toxic semaphore like this:
// cleanup dangerous semaphore with signal pending from vkAcquireNextImageKHR (tie it to a specific queue)
// https://github.com/KhronosGroup/Vulkan-Docs/issues/1059
void cleanupUnsafeSemaphore( VkQueue queue, VkSemaphore semaphore ){
const VkPipelineStageFlags psw = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &semaphore;
submit_info.pWaitDstStageMask;
vkQueueSubmit( queue, 1, &submit_info, VK_NULL_HANDLE );
}
After that the semaphore can be properly catched with a fence or vkQueueWaitIdle, and then destroyed or reused.
I just destroy them, because the new semaphore count might differ, and I don't really consider swapchain recreation a hotspot (and also I just use vkDeviceWaitIdle in such case).

Related

Why is the following a memory leak? [duplicate]

I've got code that looks like this:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.
What's the best way to remove items as you are walking a std::list?
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
You want to do:
i= items.erase(i);
That will correctly update the iterator to point to the location after the iterator you removed.
You need to do the combination of Kristo's answer and MSN's:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
Of course, the most efficient and SuperCool® STL savy thing would be something like this:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
I have sumup it, here is the three method with example:
1. using while loop
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
2. using remove_if member funtion in list:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3. using std::remove_if funtion combining with erase member function:
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
4. using for loop , should note update the iterator:
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
Use std::remove_if algorithm.
Edit:
Work with collections should be like:
prepare collection.
process collection.
Life will be easier if you won't mix this steps.
std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
std::for_each
The alternative for loop version to Kristo's answer.
You lose some efficiency, you go backwards and then forward again when deleting but in exchange for the extra iterator increment you can have the iterator declared in the loop scope and the code looking a bit cleaner. What to choose depends on priorities of the moment.
The answer was totally out of time, I know...
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
Here's an example using a for loop that iterates the list and increments or revalidates the iterator in the event of an item being removed during traversal of the list.
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
Removal invalidates only the iterators that point to the elements that are removed.
So in this case after removing *i , i is invalidated and you cannot do increment on it.
What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.
If you think of the std::list like a queue, then you can dequeue and enqueue all the items that you want to keep, but only dequeue (and not enqueue) the item you want to remove. Here's an example where I want to remove 5 from a list containing the numbers 1-10...
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList will now only have numbers 1-4 and 6-10.
Iterating backwards avoids the effect of erasing an element on the remaining elements to be traversed:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS: see this, e.g., regarding backward iteration.
PS2: I did not thoroughly tested if it handles well erasing elements at the ends.
You can write
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive) {
i = items.erase(i);
} else {
other_code_involving(*i);
i++;
}
}
You can write equivalent code with std::list::remove_if, which is less verbose and more explicit
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
The std::vector::erase std::remove_if idiom should be used when items is a vector instead of a list to keep compexity at O(n) - or in case you write generic code and items might be a container with no effective way to erase single items (like a vector)
items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
}));
do while loop, it's flexable and fast and easy to read and write.
auto textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
I'd like to share my method. This method also allows the insertion of the element to the back of the list during iteration
#include <iostream>
#include <list>
int main(int argc, char **argv) {
std::list<int> d;
for (int i = 0; i < 12; ++i) {
d.push_back(i);
}
auto it = d.begin();
int nelem = d.size(); // number of current elements
for (int ielem = 0; ielem < nelem; ++ielem) {
auto &i = *it;
if (i % 2 == 0) {
it = d.erase(it);
} else {
if (i % 3 == 0) {
d.push_back(3*i);
}
++it;
}
}
for (auto i : d) {
std::cout << i << ", ";
}
std::cout << std::endl;
// result should be: 1, 3, 5, 7, 9, 11, 9, 27,
return 0;
}
I think you have a bug there, I code this way:
for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
itAudioChannel != audioChannels.end(); )
{
CAudioChannel *audioChannel = *itAudioChannel;
std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
itAudioChannel++;
if (audioChannel->destroyMe)
{
audioChannels.erase(itCurrentAudioChannel);
delete audioChannel;
continue;
}
audioChannel->Mix(outBuffer, numSamples);
}

Member Variables in Class Get Blown Away When Using std::thread

I have defined a base class using std::thread. For the child class, I perform some initialization of member variables and then start the thread using m_thread.reset(new std::thread(&MyClass::ThreadMain, this)); where m_thread is a member of MyClass. The purpose of the class is to read data from a serial port and report to a parent. The posix message queue handle of the parent is passed to MyClass during initialization before the thread is created. On running I get exceptions and I see that member variables that were initialized before the thread started appear to be no longer valid using the watch in GDB.
It appears as if the first message on the serial port is received and passed validation in order to get to the SendToParent call. At this call, it appears that I lose the stack. I tried running cppcheck to see if I have any memory leaks or buffer overflows and found nothing.
void MyClass::ThreadMain(void)
{
ssize_t bytesRead = 0;
UINT8 buffer[256];
UINT8 message[256];
BOOL partialMessage = FALSE;
UINT8 messageIndex = 0;
UINT8 payloadLength = 0;
// read data from the UART
while(1)
{
// the UART is setup to pend until data is available
bytesRead = read(m_radioFileDescriptor, buffer, sizeof(buffer));
if (FAIL == bytesRead)
{
LOG_SYSTEM_INFO("UART Read interrupted by a system call");
}
else if (bytesRead > 0)
{
// build the message
for(ssize_t i = 0 ; i < bytesRead ; i++)
{
if (FALSE == partialMessage)
{
// have we found the start of the message?
if(START_BYTE == buffer[i])
{
// start of new message
messageIndex = 0;
message[messageIndex] = buffer[i];
partialMessage = TRUE;
messageIndex++;
}
}
else
{
// keep building the message until the expected length is reached
if(LENGTH_POSITION == messageIndex)
{
// capture the expected message length
message[messageIndex] = buffer[i];
messageIndex++;
payloadLength = buffer[i];
}
else
{
message[messageIndex] = buffer[i];
messageIndex++;
// check for expected length and end byte
if((messageIndex == payloadLength) && (END_BYTE == buffer[i]))
{
// this should be a valid message but need to confirm by checking for a valid checksum
UINT8 messageChecksum = message[messageIndex - CHKSUM_POS_FROM_END];
UINT8 calculatedChecksum = RadioProtocol::Instance().GenerateRadioChecksum(message, (payloadLength - CHKSUM_POS_FROM_END));
if (messageChecksum == calculatedChecksum)
{
SendToParent(message, payloadLength);
}
else
{
LOG_SYSTEM_ERROR("Checksum FAILURE");
}
// reset for the next message
partialMessage = FALSE;
messageIndex = 0;
}
else if((messageIndex == payloadLength) && (END_BYTE != buffer[i]))
{
// malformed message - throw out and look for start of next message
LOG_SYSTEM_ERROR("Bytes read exceeded expected message length");
partialMessage = FALSE;
messageIndex = 0;
}
}
}
} // end for loop of bytes read on the port
}
else
{
LOG_SYSTEM_INFO("Read returned 0 bytes which is unexpected");
}
}
}
void MyClass::SendToParent(UINT8* pMsg, UINT8 size)
{
if ((pMsg != NULL) && (m_parentQueueHandle > 0))
{
// message is valid - pass up for processing
MsgQueueMessage msgToSend;
msgToSend.m_msgHeader = UART_MESSASGE;
bzero(msgToSend.m_msgData, sizeof(msgToSend.m_msgData));
for (UINT8 i = 0; i < size; i++)
{
msgToSend.m_msgData[i] = pMsg[i];
}
if (FAIL == msgsnd(m_parentQueueHandle, &msgToSend, sizeof(msgToSend), IPC_NOWAIT))
{
LOG_SYSTEM_ERROR("FAILED to send message on queue");
}
}
}
This acts like I am performing a buffer overflow but I just can't see it. When I set a breakpoint at the line UINT8 messageChecksum = message[messageIndex - CHKSUM_POS_FROM_END]; all data in the watch window appear valid. If I step over to the next line then the data, m_parentQueueHandle as an example, gets blown away.
This is my first time working with c++11 threads and particularly with c++. Any help or insights would be appreciated.
I think I found the issue. I added a bunch of printfs and found that the destructor for the class was being called. Much further upstreamI had the parent object being created as a local variable and it was going out of scope. This caused the child to go out of scope but the threads were still running. I certainly need to clean up the threads in the destructor.

CView::OnFilePrint crashing MFC application

We have an MFC application that has been used and maintained for many years. Recently we made some administrative changes to some computers that are running the application. Now the software occasionally crashes when printing from the application.
We are using pretty standard MFC code to initiate the printing. We added try/catch blocks around what we felt like are the pertinent areas of the code with no luck. Whatever is failing does not seem to throw.
We get the typical dialog stating that "____ MFC Application has stopped working". Closing the program is the only option.
The windows event logger shows that our application is the Faulting application.
The exception code is 0xc0000005, which appears to be an Access Denied error.
The application is in the CView::OnFilePrint() code when the crash occurs.
We have added some logging, and we know that we get through DoPreparePrinting, and OnBeginPrinting.
We believe that CDC::StartDoc would be the next thing called, then CView::OnPrepareDC. We don't get to OnPrepareDC when we fail.
We don't seem to find the source code for CView::OnFilePrint, so we are not sure what it looks like. From research online, we think that things happen in this order in OnFilePrint:
// what we think is in OnFilePrint:
CView::OnFilePrint()
{
OnPreparePrinting(); <- we get through our override of this
OnBeginPrinting(); <- we get through our override of this
// loop back to here on multiple docs
CDC::StartDoc();
CView::OnPrepareDC(); <- we do not reach our override of this
CView::OnPaint();
CDC::EndPage();
// loop back on multiple docs
...
// finish if last doc...
}
I would like to have the source for it so we could attempt to rewrite it and try to gracefully fail instead of failing by crashing.
I'm looking for:
1) any suggestions as to how to figure out why the process of printing causes our application to crash.
2) A location for where the CView::OnFilePrint code is located, if available.
(the only idea I have left to narrow down the problem is to call our own version of this so that we can step through it and add logging and/or see if we can at least fail gracefully when it the problem occurs.)
The printer is Xerox Phaser 3610, for what its worth.
source code for CView::OnFilePrint should be in C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\viewprnt.cpp, depending on VS version. There could also be a problem with printer initialization/access.
If there is any error it is most likely due to printer initialization. You can override OnFilePrint and add CPrintInfo printInfo for testing. Example:
//ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
//ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, OnFilePrint)
void CMyView::OnFilePrint()
{
try
{
CPrintInfo printInfo;
}
catch(...)
{
//log error
AfxMessageBox(L"error");
}
CView::OnFilePrint();
}
As noted in comments, another possibility is that there is a bug somewhere else in the code, which may not necessarily be related to printing.
Inside of CView::OnFilePrint, this occurs:
CWnd * hwndTemp = AfxGetMainWnd();
It turns out that if you don't call OnFilePrint from the main thread, this returns NULL.
Due to slight timing changes when the computers were logged onto a domain, OnFilePrint was being called from another thread. This causes the above call to return null, then when this line gets executed:
hwndTemp->EnableWindow(FALSE);
The application crashes.
There are several ways to fix this. One is to use this:
CWnd * hwndTemp = AfxGetApp()->GetMainWnd();
In place of this:
CWnd * hwndTemp = AfxGetMainWnd();
Another way is to assure that OnFilePrint is only called from the main thread.
A cut to the chase version of the code in CView::OnFilePrint is here:
// disable main window while printing & init printing status dialog
// Store the Handle of the Window in a temp so that it can be enabled
// once the printing is finished
CWnd * hwndTemp = AfxGetMainWnd(); // <--- CAN RETURN NULL HERE
hwndTemp->EnableWindow(FALSE); // <--- CRASH WILL OCCUR HERE
CPrintingDialog dlgPrintStatus(this);
Full version of CView::OnFilePrint is below.
The OnFilePrint code, with the problem area noted:
void CView::OnFilePrint()
{
// get default print info
CPrintInfo printInfo;
ASSERT(printInfo.m_pPD != NULL); // must be set
if (LOWORD(GetCurrentMessage()->wParam) == ID_FILE_PRINT_DIRECT)
{
CCommandLineInfo* pCmdInfo = AfxGetApp()->m_pCmdInfo;
if (pCmdInfo != NULL)
{
if (pCmdInfo->m_nShellCommand == CCommandLineInfo::FilePrintTo)
{
printInfo.m_pPD->m_pd.hDC = ::CreateDC(pCmdInfo->m_strDriverName,
pCmdInfo->m_strPrinterName, pCmdInfo->m_strPortName, NULL);
if (printInfo.m_pPD->m_pd.hDC == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT);
return;
}
}
}
printInfo.m_bDirect = TRUE;
}
if (OnPreparePrinting(&printInfo))
{
// hDC must be set (did you remember to call DoPreparePrinting?)
ASSERT(printInfo.m_pPD->m_pd.hDC != NULL);
// gather file to print to if print-to-file selected
CString strOutput;
if (printInfo.m_pPD->m_pd.Flags & PD_PRINTTOFILE && !printInfo.m_bDocObject)
{
// construct CFileDialog for browsing
CString strDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULTEXT));
CString strPrintDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULT));
CString strFilter(MAKEINTRESOURCE(AFX_IDS_PRINTFILTER));
CString strCaption(MAKEINTRESOURCE(AFX_IDS_PRINTCAPTION));
CFileDialog dlg(FALSE, strDef, strPrintDef,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, strFilter, NULL, 0);
dlg.m_ofn.lpstrTitle = strCaption;
if (dlg.DoModal() != IDOK)
return;
// set output device to resulting path name
strOutput = dlg.GetPathName();
}
// set up document info and start the document printing process
CString strTitle;
CDocument* pDoc = GetDocument();
if (pDoc != NULL)
strTitle = pDoc->GetTitle();
else
EnsureParentFrame()->GetWindowText(strTitle);
DOCINFO docInfo;
memset(&docInfo, 0, sizeof(DOCINFO));
docInfo.cbSize = sizeof(DOCINFO);
docInfo.lpszDocName = strTitle;
CString strPortName;
if (strOutput.IsEmpty())
{
docInfo.lpszOutput = NULL;
strPortName = printInfo.m_pPD->GetPortName();
}
else
{
docInfo.lpszOutput = strOutput;
AfxGetFileTitle(strOutput,
strPortName.GetBuffer(_MAX_PATH), _MAX_PATH);
}
// setup the printing DC
CDC dcPrint;
if (!printInfo.m_bDocObject)
{
dcPrint.Attach(printInfo.m_pPD->m_pd.hDC); // attach printer dc
dcPrint.m_bPrinting = TRUE;
}
OnBeginPrinting(&dcPrint, &printInfo);
if (!printInfo.m_bDocObject)
dcPrint.SetAbortProc(_AfxAbortProc);
/**********************************************************************
Problem area.
If the calling thread is not the main thread, the call to AfxGetMainWnd
can return NULL. In this case, hwndTemp->EnableWindow(FALSE) will crash
the application.
**********************************************************************/
// disable main window while printing & init printing status dialog
// Store the Handle of the Window in a temp so that it can be enabled
// once the printing is finished
CWnd * hwndTemp = AfxGetMainWnd(); // <--- CAN RETURN NULL HERE
hwndTemp->EnableWindow(FALSE); // <--- CRASH WILL OCCUR HERE
CPrintingDialog dlgPrintStatus(this);
CString strTemp;
dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_DOCNAME, strTitle);
dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PRINTERNAME,
printInfo.m_pPD->GetDeviceName());
dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PORTNAME, strPortName);
dlgPrintStatus.ShowWindow(SW_SHOW);
dlgPrintStatus.UpdateWindow();
// start document printing process
if (!printInfo.m_bDocObject)
{
printInfo.m_nJobNumber = dcPrint.StartDoc(&docInfo);
if (printInfo.m_nJobNumber == SP_ERROR)
{
// enable main window before proceeding
hwndTemp->EnableWindow(TRUE);
// cleanup and show error message
OnEndPrinting(&dcPrint, &printInfo);
dlgPrintStatus.DestroyWindow();
dcPrint.Detach(); // will be cleaned up by CPrintInfo destructor
AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT);
return;
}
}
// Guarantee values are in the valid range
UINT nEndPage = printInfo.GetToPage();
UINT nStartPage = printInfo.GetFromPage();
if (nEndPage < printInfo.GetMinPage())
nEndPage = printInfo.GetMinPage();
if (nEndPage > printInfo.GetMaxPage())
nEndPage = printInfo.GetMaxPage();
if (nStartPage < printInfo.GetMinPage())
nStartPage = printInfo.GetMinPage();
if (nStartPage > printInfo.GetMaxPage())
nStartPage = printInfo.GetMaxPage();
int nStep = (nEndPage >= nStartPage) ? 1 : -1;
nEndPage = (nEndPage == 0xffff) ? 0xffff : nEndPage + nStep;
VERIFY(strTemp.LoadString(AFX_IDS_PRINTPAGENUM));
// If it's a doc object, we don't loop page-by-page
// because doc objects don't support that kind of levity.
BOOL bError = FALSE;
if (printInfo.m_bDocObject)
{
OnPrepareDC(&dcPrint, &printInfo);
OnPrint(&dcPrint, &printInfo);
}
else
{
// begin page printing loop
for (printInfo.m_nCurPage = nStartPage;
printInfo.m_nCurPage != nEndPage; printInfo.m_nCurPage += nStep)
{
OnPrepareDC(&dcPrint, &printInfo);
// check for end of print
if (!printInfo.m_bContinuePrinting)
break;
// write current page
TCHAR szBuf[80];
ATL_CRT_ERRORCHECK_SPRINTF(_sntprintf_s(szBuf, _countof(szBuf), _countof(szBuf) - 1, strTemp, printInfo.m_nCurPage));
dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PAGENUM, szBuf);
// set up drawing rect to entire page (in logical coordinates)
printInfo.m_rectDraw.SetRect(0, 0,
dcPrint.GetDeviceCaps(HORZRES),
dcPrint.GetDeviceCaps(VERTRES));
dcPrint.DPtoLP(&printInfo.m_rectDraw);
// attempt to start the current page
if (dcPrint.StartPage() < 0)
{
bError = TRUE;
break;
}
// must call OnPrepareDC on newer versions of Windows because
// StartPage now resets the device attributes.
OnPrepareDC(&dcPrint, &printInfo);
ASSERT(printInfo.m_bContinuePrinting);
// page successfully started, so now render the page
OnPrint(&dcPrint, &printInfo);
if ((nStep > 0) && // pages are printed in ascending order
(nEndPage > printInfo.GetMaxPage() + nStep)) // out off pages
{
// OnPrint may have set the last page
// because the end of the document was reached.
// The loop must not continue with the next iteration.
nEndPage = printInfo.GetMaxPage() + nStep;
}
// If the user restarts the job when it's spooling, all
// subsequent calls to EndPage returns < 0. The first time
// GetLastError returns ERROR_PRINT_CANCELLED
if (dcPrint.EndPage() < 0 && (GetLastError()!= ERROR_SUCCESS))
{
HANDLE hPrinter;
if (!OpenPrinter(LPTSTR(printInfo.m_pPD->GetDeviceName().GetBuffer()), &hPrinter, NULL))
{
bError = TRUE;
break;
}
DWORD cBytesNeeded;
if(!GetJob(hPrinter,printInfo.m_nJobNumber,1,NULL,0,&cBytesNeeded))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
bError = TRUE;
break;
}
}
JOB_INFO_1 *pJobInfo;
if((pJobInfo = (JOB_INFO_1 *)malloc(cBytesNeeded))== NULL)
{
bError = TRUE;
break;
}
DWORD cBytesUsed;
BOOL bRet = GetJob(hPrinter,printInfo.m_nJobNumber,1,LPBYTE(pJobInfo),cBytesNeeded,&cBytesUsed);
DWORD dwJobStatus = pJobInfo->Status;
free(pJobInfo);
pJobInfo = NULL;
// if job status is restart, just continue
if(!bRet || !(dwJobStatus & JOB_STATUS_RESTART) )
{
bError = TRUE;
break;
}
}
if(!_AfxAbortProc(dcPrint.m_hDC, 0))
{
bError = TRUE;
break;
}
}
}
// cleanup document printing process
if (!printInfo.m_bDocObject)
{
if (!bError)
dcPrint.EndDoc();
else
dcPrint.AbortDoc();
}
hwndTemp->EnableWindow(); // enable main window
OnEndPrinting(&dcPrint, &printInfo); // clean up after printing
dlgPrintStatus.DestroyWindow();
dcPrint.Detach(); // will be cleaned up by CPrintInfo destructor
}
}

Accidently deleting entire linked list when trying to delete the head

I'm working on a checker's simulation game for my C++ class. My issue is with the linked list that holds the checkers. I can delete any checker perfectly with the exception of the head of the list. I've looked around here and other websites and I believe there's a memory leak somewhere. I'm fairly new to C++ so I'm not sure what to really do other than playing around with things (which will probably just create a bigger problem). I've never posted here before, so excuse me if the formatting is slightly off or too messy. I'll try to make it brief. First, here's a snippet of the node class for the linked list.
class CheckerpieceNode
{
private:
Checkerpiece *Node;
CheckerpieceNode *Next;
public:
CheckerpieceNode(); // sets Node and Next to NULL in .cpp file
void setNode(Checkerpiece *node);
void setNext(CheckerpieceNode *next);
Checkerpiece* getNode();
CheckerpieceNode* getNext();
};
And the functions are set up pretty much as you would expect in a Checkerpiece.cpp class.
Here's how the code is used. Its called by a Checkerboard object in my main class.
theCheckerboard.removeChecker(theCheckerboard.findChecker(selector->getCurrentX() + 0, selector->getCurrentY() - VERTICAL_SHIFT, listHead), listHead);
The VERTICAL_SHIFT simply has to do with the way my checkerboard graphic is on the console. Since it works perfectly for all other nodes (excluding the head) I've ruled it out as a source of error. Selector is a checkerpiece object but its not part of the list.
Here's the actual findChecker and removeChecker code from Checkerboard class.
Checkerpiece* findChecker(int x, int y, CheckerpieceNode* list_head)
{
if(list_head== NULL) return NULL; // do nothing
else
{
CheckerpieceNode* node = new CheckerpieceNode;
node = list_head;
while(node != NULL && node->getNode() != NULL)
{
if()// comparison check here, but removed for space
{
return node->getNode();
delete node;
node = NULL;
}
else // traversing
node = node->getNext();
}
return NULL;
}
}
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head)
{
if(list_head== NULL) // throw exception
else
{
CheckerpieceNode *temp = NULL, *previous = NULL;
Checkerpiece* c_checker= new Checkerpiece;
temp = list_head;
while(temp != NULL && temp->getNode() != NULL)
{
c_checker= temp->getNode();
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->getNext();
}
else
{
if(temp != list_head)
{
previous->setNext(temp->getNext());
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->getNext();
delete temp;
temp = NULL;
}
return;
}
}
}
}
Oh my, you're complicating it. Lots of redundant checks, assignments and unnecessary variables (like c_checker which leaks memory too).
// Write down the various scenarios you can expect first:
// (a) null inputs
// (b) can't find d_checker
// (c) d_checker is in head
// (d) d_checker is elsewhere in the list
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head) {
// first sanitize your inputs
if (d_checker == nullptr || list_head == nullptr) // use nullptr instead of NULL. its a keyword literal of type nullptr_t
throw exception;
// You understand that there is a special case for deleting head. Good.
// Just take care of it once and for all so that you don't check every time in the loop.
CheckerpieceNode *curr = list_head;
// take care of deleting head before traversal
if (d_checker == curr->getNode()) {
list_head = list_head->next; // update list head
delete curr; // delete previous head
return; // we're done
}
CheckerpieceNode *prev = curr;
curr = curr->next;
// traverse through the list - keep track of previous
while (curr != nullptr) {
if (d_checker == curr->getNode()) {
prev->next = curr->next;
delete curr;
break; // we're done!
}
prev = curr;
curr = curr->next;
}
}
I hope that helps. Take the time to break down the problem into smaller pieces, figure out the scenarios possible, how you'll handle them and only then start writing code.
Based on this edit by the question author, the solution he used was to:
I modified the code to show the address passing in the checker delete
function.
void delete_checker(Checker* d_checker, CheckerNode* &list_head) // pass by address
{
if(list_head== NULL) // throw exception
else
{
CheckerNode*temp = NULL, *previous = NULL;
Checker* c_checker= new Checker;
temp = list_head;
while(temp != NULL && temp->node!= NULL)
{
c_checker= temp->node;
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->next;
}
else
{
if(temp != list_head)
{
previous->next = temp->next;
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->next;
delete temp;
temp = NULL;
}
delete c_checker;
c_checker = nullptr;
return;
}
}
}
}
removeChecker cannot modify the value of list_head as it is past by value. The method signature should be:
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode** list_head)
// You will need to call this function with &list_head
or
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* &list_head)
// Calling code does not need to change

Natively buffering audio

This probably is one of my own mistakes, but I can't seem to find what is wrong. After trying to improve performance of my application, I moved audio buffering from the Java layer to the native layer. Audio handling (recording/playing) is already done natively using the OpenSL ES API.
Yet the native buffering is causing my application to crash whenever I start the application. I use a simple Queue implementation as my buffer, where the first node is the oldest data (FIFO).
struct bufferNode{
struct bufferNode* next;
jbyte* data;
};
struct bufferQueue{
struct bufferNode* first;
struct bufferNode* last;
int size;
};
The audio data is referred to by the jbyte* in the bufferNode. Access to the Queue is done via these two methods, and is synchronized with a mutex.
void enqueueInBuffer(struct bufferQueue* queue, jbyte* data){
SLresult result;
if(queue != NULL){
if(data != NULL){
result = pthread_mutex_lock(&recMutex);
if(result != 0){
decodeMutexResult(result);
logErr("EnqueueInBuffer", "Unable to acquire recording mutex");
} else {
struct bufferNode* node = (struct bufferNode*)malloc(sizeof(struct bufferNode));
if(node == NULL){
logErr("EnqueueInBuffer", "Insufficient memory available to buffer new audio");
} else {
node->data = data;
if(queue->first == NULL){
queue->first = queue->last = node;
} else {
queue->last->next = node;
queue->last = node;
}
queue->size = queue->size + 1;
node->next = NULL;
}
}
result = pthread_mutex_unlock(&recMutex);
if(result != 0){
decodeMutexResult(result);
logErr("EnqueueInBuffer", "Unable to release recording mutex");
}
} else {
logErr("EnqueueInBuffer", "Data is NULL");
}
} else {
logErr("EnqueueInBuffer", "Queue is NULL");
}
}
void dequeueFromBuffer(struct bufferQueue* queue, jbyte* returnData){
SLresult result;
result = pthread_mutex_lock(&recMutex);
if(result != 0){
decodeMutexResult(result);
logErr("DequeueFromBuffer", "Unable to acquire recording mutex");
} else {
if(queue->first == NULL){
returnData = NULL;
} else {
returnData = queue->first->data;
struct bufferNode* tmp = queue->first;
if(queue->first == queue->last){
queue->first = queue->last = NULL;
} else {
queue->first = queue->first->next;
}
free(tmp);
queue->size = queue->size - 1;
}
}
result = pthread_mutex_unlock(&recMutex);
if(result != 0){
decodeMutexResult(result);
logErr("DequeueFromBuffer", "Unable to release recording mutex");
}
}
Where the log and decode methods are selfdeclared utility methods. Log just logs the message to the logcat, while the decode methods "decode" any error number possible from the previous method call.
Yet I keep getting an error when I try to enqueue audio data. Whenever I call the enqueueInBuffer method, I get a SIGSEGV native error, with code 1 (SEGV_MAPERR). Yet I can't seem to find what is causing the error. Both the Queue and the audio data exist when I try to make the enqueueInBuffer method call (which is done in an OpenSL ES Recorder callback, hence the synchronization).
Is there something else going on what causes the segmentation fault? Probably I am responsible for it, but I can't seem to find the error.
Apparently, this was caused by a line of code I have in my OpenSL ES Recorder callback.
The callback originally looked like this:
void recorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
SLresult result;
enqueueInBuffer(&recordingQueue, (*recorderBuffers[queueIndex]));
result = (*bq)->Enqueue(bq, recorderBuffers[queueIndex], RECORDER_FRAMES * sizeof(jbyte));
if(checkError(result, "RecorderCallB", "Unable to enqueue new buffer on recorder") == -1){
return;
}
queueIndex = queueIndex++ % MAX_RECORDER_BUFFERS;
}
However, it seems that the last line of the callback didn't correctly create the new index. The buffers I use are in an array, which is 4 long.
Changing the last line to
queueIndex = (queueIndex + 1) % MAX_RECORDER_BUFFERS;
seems to have fixed the error.

Resources