2016-06-15 4 views
3

대리인의 GC를 피할 수 있습니까?대리인의 GC를 피할 수 있습니까?

저는 작업 시스템을 구축 중입니다. 로컬 작업 큐가있는 N 스레드가 있습니다. 작업 큐는 기본적으로 단지 Array!Fiber tasks입니다. 광섬유를 다른 스레드로 전송하는 것은 바람직하지 않기 때문에 스레드에 클로저/위임자를 보내고 해당 위임자로부터 파이버를 만들고 배열 tasks에 넣습니다.

이제 대리자는 변수를 캡처하는 대리자입니다.

//Some Pseudo code 

auto f = //some function; 
auto cell = Cell(...); 

auto del =() { 
    let res = f(); 
    cell.write(res); 
} 

send(del); 

}

이제 셀 힙 할당 원자 카운터와 동기화된다. 원자 카운터가 cell에 도달했는지 확인하면 0에 도달했는지를 확인할 수 있습니다.

변수를 캡처하는 대리자가 GC에 변수를 할당하는 것이 문제입니다. 이제 포인터를 할당하기 만해도 큰 문제는 아니지만 여전히 GC를 피하고 싶습니다.

어떻게하면됩니까?

답변

8

당신은 이미이 모든 것을 알고있을 것입니다. 그러나 이것은 약간의 FAQ이므로 약간의 세부 사항을 작성하려고합니다.

먼저, 대리인이 무엇인지 이해해 봅시다. 슬라이스가 길이와 쌍을 이루는 C 데이터 포인터 인 것처럼, 델리게이트는 함수 포인터와 쌍을 이루는 C 데이터 포인터 일뿐입니다. 이

struct d_delegate { 
    void* ptr; // yes, it is actually typed void*! 
    T* funcptr; // this is actually a function pointer 
}; 

정의 된 것처럼 이들은, 그들에게 기대하는 기능과 함께 전달된다 (중첩 된 위임을 할 때 일부 컴파일러 오류 뒤에 이유가 단지 하나의 데이터 PTR 있다는 사실합니다 !이 시점에서

Object obj = new Object(); 
string delegate() dg = &obj.toString; 

,-dg.ptr 포인트 : 슬라이스 void* 데이터를 가리키는 것입니다

그건) 클래스 메소드 내에서와 마찬가지로 다양한 장소에서 올 수, 가비지 수집 클래스 개체가 발생하지만, 그 이유는 내가 위에 new 그것을 편집했기 때문입니다. 이 경우

struct MyStruct { 
    string doSomething() { return "hi"; } 
} 

MyStruct obj; 

string delegate() dg = &obj.doSomething; 

인해 dg.ptr는 또한 임시 개체를 가리 키도록 내가 위를 할당하는 방법을 스택에 obj 살고있다.

무언가가 위임자이든 아니든간에 사용 된 메모리 할당 체계에 대해 아무 말도하지 않습니다. 전달 된 대리자가 끝내기 전에 사라질 임시 개체를 가리킬 수 있으므로 틀림없이 위험합니다. (그런 이유로 GC가 그런 사용 후 자유로운 버그를 방지하는 데 도움이되는 주된 이유입니다.)

따라서 어떤 대리인이 어떤 객체에서 왔는지 알 수있는 이유는 무엇입니까? 음, 자동으로 생성 된 클로저는 컴파일러가 델리 게이트의 수명이 외부 함수보다 길다고 생각할 때 로컬 변수를 GC 세그먼트에 복사 할 수 있습니다.여기

void some_function(void delegate() dg); 

void foo() { 
    int a; 
    void nested() { 
     a++; 
    } 
    some_function(&nested); 
} 

, 그것의 복사본을 보관합니다 some_function을 가정하고 처리 할 때 use-after-free 고통이다 버그 (방지하고자하기 때문에 GC 세그먼트에 변수 a을 복사합니다 컴파일러는 자주로 디버깅하는 방법 메모리 손상을 유발합니다!)뿐만 아니라 메모리 누수가 발생합니다. 당신은 당신이 대리자 정의에 scope 키워드를 사용하여 스스로를 잘 할 것 컴파일러를 약속 경우

그러나, 당신을 신뢰하고 그들이 어디에 바로 지역 주민을 떠나 :

void some_function(scope void delegate() dg); 

상태 유지 나머지는 동일하게, 더 이상 사본을 할당하지 않습니다. 함수 정의 측면에서 수행하는 것이 가장 좋습니다. 함수 작성자는 실제로 사본을 보관하지 않을 수 있기 때문입니다. 지역 변수의 주소가 중첩 된 기능에 의해 사용되는 경우

void foo() { 
    int a; 
    void nested() { 
     a++; 
    } 
    // this shouldn't allocate either 
    scope void delegate() dg = &nested; 
    some_function(&dg); 
} 

그래서, 유일한 시간 메모리가 자동으로 GC에 의해 할당된다

하지만 사용 측면에서

, 당신은 또한 그것의 범위에 레이블을 지정할 수 있습니다 없이scope 키워드를 가져 왔습니다.

() => whatever() { return foo; } 구문은 주소가 자동으로 지정되는 명명 된 중첩 된 함수의 단순한 축약이므로 위와 같은 방식으로 작동합니다. dg = {a++;};은 위의 dg = &nested;과 동일합니다.

따라서 위임자가 수동으로 할당하려는 경우 변수를 자동으로 캡처하는 대신 수동으로 개체를 할당하고 해당 메서드 중 하나에서 대리인을 만들어야합니다. 그러나 평생을 추적하고 올바르게 해제해야합니다. 그것은 까다로운 부분입니다. 당신 그래서 예를 들면

: 당신이 돈 그렇게 할 때

struct Helper { 
    T res; 
    void del() { 
     cell.write(res); 
    } 
} 

Helper* helper = malloc(Helper.sizeof); 
helper.res = res; // copy the local explicitly 

send(&helper.del); 

그런 다음, 수신 측에 free(dg.ptr);하는 것을 잊지 마세요 :

auto del =() { 
    let res = f(); 
    cell.write(res); 
}; 

당신이이로 번역 할 수 그것을 유출하지 마십시오.

send을 실제로 Helper 개의 객체로 변경하면 더 이상 할당 할 필요가 없습니다. 값으로 전달할 수 있습니다.


또한 당신이 자리에서 다른 데이터를 전달하기 위해 그 포인터에 다른 데이터를 포장 할 수 있지만,이 ABI 해킹 및 정의되지 않은 행동이 될 거라고 나에게 발생합니다. 비록 당신이 원한다면 그것을 시도하십시오 :)

+0

위대한 설명 감사. –

관련 문제