none
PPL 사용시 operator new, delete 관련하여 버그가 의심됩니다 RRS feed

  • 질문

  • 개발환경 : VisualStudio 2015, Windows 10

    안녕하세요.

    PPL을 처음 사용해봅니다.

    아래와 같이 간단한 PPL 소스코드를 작성 했습니다.

    #include <ppl.h>
    #include <concurrent_vector.h>

    using namespace concurrency;
    concurrent_vector<int> myBag;

    concurrency::parallel_for(0, 1000000, [&](int i)
    {
    myBag.push_back(ㅑ);
    }
    );


    저의 프로젝트에는 operator new , delete가 재정의된 메모리풀을 사용하고 있습니다.

    new를 할 때 4바이트 만큼 더 생성하여 맨 앞에 버퍼 크기를 저장하는 방식을 사용중입니다.

    직접 만든 new와 delete에 브레이크 포인터를 걸고

    parallel_for 함수를 실행하면 제가 만든 delete 함수쪽으로 호출되는 것을 확인할 수 있습니다

    new는 호출되지 않았습니다.

    (아마도 PPL 안쪽에서는 직접 만든 new를 사용하는 것 같습니다만 delete는 그렇지 않는 것으로 보입니다)

    결과적으로 잘못된 delete 수행에 의해 크래시가 발생됩니다.

    ( 맨 앞 4바이트 만큼의 버퍼 사이즈에 대한 부분이 없기때문)

    이 문제를 어떻게 해결하면 좋을까요?


    • 편집됨 꿀시럽 2018년 4월 5일 목요일 오전 6:53
    2018년 4월 5일 목요일 오전 6:50

모든 응답

  • 혹시 https://msdn.microsoft.com/en-us/library/dd998050.aspx 문서를 참조해 보시는 것이 어떨지요?
    2018년 4월 6일 금요일 오전 1:21
  • #include <windows.h>
    #include <iostream>
    #include <ppl.h>
    #include <concurrent_vector.h>
    
    using namespace std;
    
    void* operator new(size_t sz) {
    	
    
    	sz += sizeof(int);
    	auto* p = malloc(sz);
    
    	int* MemSize = (int*)p;
    	*MemSize = (int)sz;
    
    	cout << "my operator new: " << sz << endl;
    	return p;
    }
    
    void operator delete(void* p) {
    
    	if (p == 0)
    		return;
    	
    	int GetMemSize = *(int*)p;
    	cout << "my operator delete :" << GetMemSize << endl;
    	free(p);
    }
    
    int main()
    {
    	{
    		cout << "operator new/delete Check" << endl;
    		int* p = new int;
    		delete p;
    
    		cout << "--------------------------------" << endl;
    
    		using namespace concurrency;
    		concurrency::concurrent_vector<int> myBag;
    
    		concurrency::parallel_for(0, 1000000, [&](int i)
    		{
    			myBag.push_back(i);
    		}
    		);
    
    		cout << "myBagSize : " << myBag.size() << endl;
    	}
    
    	getchar();
        return 0;
    }
    
    

    결과:

    my operator new: 16427
    my operator new: 32811
    my operator new: 65579
    my operator new: 131115
    my operator new: 262187
    my operator new: 524331
    my operator new: 1048619
    my operator new: 2097195
    myBagSize : 1000000
    my operator delete :2097195
    my operator delete :1048619
    my operator delete :524331
    my operator delete :262187
    my operator delete :-84215046

    ...

    ..

    .



    샘플 소스를 작성 해봤습니다.

    정상적으로 delete가 진행되는 과정중 마지막 줄의 -84215046의 경우 제가 만든 new를 통해 만들어진 것이 아닌 것 같습니다.



    2018년 4월 6일 금요일 오전 4:34
  • 릴리즈 x86 으로 수행하면 이런 결과가 나오기도 하네요

    operator new/delete Check
    my operator new: 8
    my operator delete :8
    --------------------------------
    my operator new: 12
    my operator new: 20
    my operator new: 12
    my operator new: 68
    my operator new: 36
    my operator new: 132
    my operator new: 260
    my operator new: 516
    my operator new: 1028
    my operator new: 2052
    my operator new: 4135
    my operator new: 8231
    my operator new: 16423
    my operator new: 32807
    my operator new: 65575
    my operator new: 131111
    my operator new: 262183
    my operator new: 524327
    my operator new: 1048615
    my operator new: 2097191
    myBagSize : 1000000
    my operator delete :2097191
    my operator delete :1048615
    my operator delete :524327
    my operator delete :262183
    my operator delete :131111
    my operator delete :65575
    my operator delete :32807
    my operator delete :16423
    my operator delete :8231
    my operator delete :4135
    my operator delete :500064
    my operator delete :625024
    my operator delete :125016
    my operator delete :500006
    my operator delete :17
    my operator delete :1
    my operator delete :375001
    my operator delete :375000
    my operator delete :250000
    my operator delete :875000

    2018년 4월 6일 금요일 오전 4:36
  • PPL의 버그가 아니라 new/delete operator 를 잘못 구현하셔서 그런겁니다.

    operator new 에서 전달된 사이즈보다 4byte 더 할당하여 추가 정보를 저장한다고 하더라도

    반환하는 포인터는 추가 정보가 담긴 주소가 아닌 실제 데이터 파트 주소를 반환해야 합니다.

    고쳐본다면 대략 아래와 같겠죠.

    void* operator new(size_t sz) {
    
    	sz += sizeof(int);
    	auto* p = malloc(sz);
    
    	int* MemSize = (int*)p;
    	*MemSize = (int)sz;
    
    	cout << "my operator new: " << sz << endl;
    	return (int*)p + 1;
    }

    이렇게 하지 않으면 올려주신 예제처럼 delete 하려는 주소가 꼬여버리는 현상이 발생할 수 있고,

    virtual 함수를 포함한 클래스 객체의 경우에는 가상 함수 테이블 위치와 겹쳐버리는 문제도 생깁니다.

    operator delete 에서는 반대로 실제 넘어온 주소는 데이터 파트의 주소이니, 여기서 - 4byte 계산한 주소를 해제하면 됩니다.

    void operator delete(void* p) {
    
    	if (p == 0)
    		return;
    
    	int GetMemSize = *((int*)p - 1);
    	cout << "my operator delete :" << GetMemSize << endl;
    	free((int*)p - 1);
    }



    2018년 4월 6일 금요일 오전 6:39
  • 네 맞네요.

    실제 사용중인 소스는 말씀하시는 것과 동일하게 되있는게 맞고 제가 샘플을 작성하면서 잘못했네요

    말씀하신것과 같이 고치면 정상적인 결과가 나오네요.

    제가 문제라고 파악했던 부분만 다뤘는데 다른 문제가 더 있나 봅니다


    다시 더 살펴보겠습니다!!

    감사합니다.

    2018년 4월 6일 금요일 오전 7:13