shared_ptr 객체를 멀티스레드에서 사용할 때

분명 shared_ptr은 레퍼런스 카운트가 스레드 안정적이라고 되어있다고 나와있지만 아래와같은 코드를 실행하면 실행하자마자 에러가 발생한다.

#include <memory>

std::shared_ptr<int> g;

DWORD WINAPI U(void *)
{
    while (true)
    {
        std::shared_ptr<int> data = g;
    }
    return 0;
}

DWORD WINAPI C(void *)
{
    while (true)
    {
        std::shared_ptr<int> data = std::make_shared<int>(0);

        g = data;
    }
    return 0;
}

int main()
{
    HANDLE h1 = CreateThread(NULL, 0, U, NULL, 0, NULL);
    HANDLE h2 = CreateThread(NULL, 0, C, NULL, 0, NULL);

    WaitForSingleObject(h1, -1);
    WaitForSingleObject(h2, -1);

    return 0;
}

거의 수시간동안 구글링해도 한결같이 나오는건 레퍼런스 카운트는 스레드 안전하다는 것 정도.
그래도 다행이도 계속 검색해보니 결국 아래와같이 atomic을 사용하는 코드가 나왔다.

#include <atomic>
#include <memory>

std::shared_ptr<int> g;

DWORD WINAPI U(void *)
{
    while (true)
    {
        std::shared_ptr<int> data = std::atomic_load(&g);
    }
    return 0;
}

DWORD WINAPI C(void *)
{
    while (true)
    {
        std::shared_ptr<int> data = std::make_shared<int>(0);
        std::atomic_store(&g, data);
    }
    return 0;
}

int main()
{
    HANDLE h1 = CreateThread(NULL, 0, U, NULL, 0, NULL);
    HANDLE h2 = CreateThread(NULL, 0, C, NULL, 0, NULL);

    WaitForSingleObject(h1, -1);
    WaitForSingleObject(h2, -1);

    return 0;
}

저렇게 atomic_store, atomic_load를 사용하니 이제 정상적으로 shared_ptr 객체가 두 스레드간의 참조가 문제없이 되었다. 분명 부스트의 shared_ptr 기준으로 코드를 보면 InterlockedIncrement 함수를 사용하여 카운트하던데 그마저도 저런식으로 사용하니 0xDDDDDDDD로 채워진 이미 초기화 된 변수가 나타났다. 좀처럼 이해하기 힘든 동작이였다.

수시간, 그리고 며칠동안 구글링 아무리 해봐도 shared_ptr는 참조 카운트가 스레드 안전하다고 할 뿐 좀 더 자세한 내용 잘 나오지도않고(…) 그냥 이렇게 사용해야 정말 안전하다 정도로 알고 넘어가야겠다 -_-;;;

shared_ptr 객체를 멀티스레드에서 사용할 때 의문점

ByteBuffer 클래스를 만들고 매소드 get, size 선언. private 맴버를 d, s를 두어 아래와같이 구현하였다.

#include <algorithm>

ByteBuffer::ByteBuffer(char *data, int size)
{
    if(size == 0)
    {
        d = nullptr;
        s = 0;
    }
    else if(data == nullptr)
    {
        d = new char[size];
        s = size;
    }
    else
    {
        d = new char[size];
        s = size;

        std::copy(data, data + size, d);
    }
}

ByteBuffer::~ByteBuffer()
{
    delete[] d;
}

char *ByteBuffer::get()
{
    return d;
}

int ByteBuffer::size()
{
    return s;
}

Continue reading shared_ptr 객체를 멀티스레드에서 사용할 때 의문점

최근 NVENC로하고있는 삽질

팀뷰어나 VNC로는 클라이언트에서 하드웨어 가속을 사용하지 못해서인지 모바일에서나 노트북에서나 발열이 좀 많다는게 단점. 특히나 게임은 화면 업데이트 빈도가 높아서인지 팀뷰어가 최적화 잘되어있지만 그래도 사용할 수 있는 대역폭에 비하면 화질이 좋지 못하였다.

아이폰이 GIF를 지원하지않는건 GIF는 하드웨어가속을 사용할 수 없기때문이라는데, 왠지 원격을 H.264로 스트리밍하고 제어한다면 노트북에서도 낮은 프로세서 사용률로 실시간으로 게임도 스트리밍하여 컨트롤 할 수 있지 않을까싶어 거진 2주간 NVENC와 XDGI 삽질을 하였다. DirectX 인터페이스나 미디어 스팩이나 아무것도 모르고 시작해서 지금도 아는게 없는상태에서 그냥 무작정 해보아서 이제야 막 결과가 눈에 들어오는 수준 orz

일단 결과는 간략하게 아래와같다.

%ec%ba%a1%ec%b2%98

인코딩 & 스트리밍 프로그램은 보여지는 화면이 없고 아직 MPEG4의 정상적인 스트리밍 방법을 모르기에 그냥 데이터만 있어도 재생이 되는 ffplay로 인코딩 된 raw 데이터를 바로 뿌려보았다. (파일로 출력하고 ffplay에서 읽도록 하면 거의 0.5초 정도 수준으로 지연이 있지만 tcp로 전송하니 버퍼링이 있어서인지 1초 넘게 지연이 생겨버린다.) 정상적으로 게임 화면이 나타나긴하지만 코딩하며 실행하면서 그래픽 메모리를 잘못 건든것이 있는지 종종 아래처럼 처참하게 깨지는 화면이 나타난다는건 덤.

2016-10-01

도중에 AAC 가속도 재공되는 인텔의 Media Codec을 사용해보려했지만 그건 나중으로 미루고 일단 NVENC를 사용하여 스트리밍하는것 부터 완벽하다 싶으면 건들어봐야겠다. 과연 완성이나 되련지 모르겠지만 완성되면 코드를 공개하던지 해봐야겠다. 아마도 ffmpeg 라이브러리를 사용해서 인코딩 하고 스트리밍 활용하는 방향으로 전환될수도 있지만 그건 나중 문제(…)

아래 화면은 그래픽카드의 비디오 엔진 점유율과 스트리밍 CPU 점유율이다. Continue reading 최근 NVENC로하고있는 삽질

윈도 환경에서 v8 빌드

이번에도 Visual Studio Community 2015이 설치되어있다는 조건하에 진행.

v8의 위키 페이지(https://github.com/v8/v8/wiki)에서 보면 빌드 방법으로는 GN을 사용하는 것. 그리고 deprecated가 된 Gyp가있다. 무엇을 사용하든 depot_tools라는것이 필수.

예전에 한번 심심풀이로 빌드해봤을때는 gyp설치가 잘 안되어 고생을 좀 했지만 큰 문제없이 hello world를 빌드했는데… 지금은 gyp가 deprecated로 표시되어있어 gn을 사용해 보려하니 관련 내용이 부실해서 크게 애먹었다(…).

그래서 gyp를 사용하는걸 더 추천하고싶고, gn은 단점이있어 그다지 추천하고싶지않다. Continue reading 윈도 환경에서 v8 빌드

ffserver for Windows 64bit 빌드

rtsp 테스트를위해 윈도 서버에 ffserver를 올릴 일이 생겼다. ffmpeg 홈페이지에서 받을 수 있는 윈도용 바이너리는 ffserver가 없다. 반나절 msys에서 ‘configure –toolchain=msvc’로 아무리 컨피그를 해봐도 ffserver가 빌드 안되길레 구글링 해봤더니 Cygwin에서 빌드된다는 문서가 나왔다. 그것도 밤 12시 쯤 집에서 마지막으로 한번 더 찾아봤을때서야 찾게되었다(…).

암튼 빌드 과정은 아래와같고 64비트 버전 기준이다.

 

1. Cygwin 설치

https://cygwin.com/install.html에서 Cygwin 다운로드. (OS가 64비트라서 64비트 버전을 받았다.)

– setup-x86_64 실행. 왠만한건 Next하면 되며, Local Package Directory에서 에러가 날 수 있지만 무시하면 된다.

– 아래 화면에서 속도가 잘 나오도록 daum 미러서버를 선택.

Cygwin select mirror

– Select Packages에서 Devel Category를 펼친다음 아래 항목들을 찾아서(Search에 입력하여 검색 가능) 선택

gcc-core, gcc-g++, git, make, yasm

– Editors에서 vim을 선택하여 ‘다음(N)’ 선택

 

2. 소스코드 다운로드
소스코드 다운로드 시 3.1.2버전을 받도록 하였다. 이유는 다음 버전부터 ffserver가 제거되기때문이다.

– 시작메뉴에서 Cygwin64 Terminal을 찾아서 실행

– git config core.autocrlf 실행하여 false가 출력되는지 확인

– true일경우 git config –global core.autocrlf false 실행, 이후 다시 git config core.autocrlf 실행하면 false가 출력됨

– git clone –branch n3.1.2 https://git.ffmpeg.org/ffmpeg.git 실행하여 소스코드 다운로드

– ffmpeg 폴더로 이동 (cd ffmpeg)

– ./configure –prefix=../ffmpeg-3.1.2 실행

– vi config.h 실행 (vim 사용법을 모른다면 구글링을)
핵심으로는 i 누르면 입력(끼워넣기)모드, ESC누르면 커서이동모드, 커서이동모드에서는 / 입력 후 검색할 단어 입력 가능, 커서이동모드에서 : 입력 후 wq 입력하면 저정 후 종료 정도이다. (검색어, wq입력 후 엔터는 필수)

– HAVE_ARC4RANDOM의 값을 1에서 0으로 수정 (링크에서 나는 에러 패치)

– #ifndef 다음라인(약 3~4번째 라인)에 _XOPEN_SOURCE 700 추가 (sigaction 함수선언 문제 해결)

– 저장 후 종료

– make -j5 실행 (-j 옵션은 동시빌드 개수. i7이면 8정도로 해도 무난하다.)

– make install 실행

3. 완료

아마 위 대로만 진행하면 에러없이 c드라이브의 cygwin64 – home – iruis – ffmpeg-3.1.2 폴더에서  ffserver.exe 파일을 얻을 수 있다. 단, cygwin64 – bin 폴더에서 cygwin1.dll 파일을 ffserver.exe 파일이 있는 폴더에 복사해둬야된다.

그러면 프롬프트상에서 ffserver를 실행하면 아래와같이 실행되는 ffserver 결과를 볼 수 있다. 설정은 다루지 않을것이기때문에 잘 설명 된 자료를 찾아서 ffserver를 사용하면 될것이다.

ffserver

 

그나저나 구글링을 더 심도있게하면 이미 빌드된 윈도용 ffserver를 받을 수 있는건 아니겠지(…)

 

참고:
https://ffmpeg.org/download.html (소스 저장소 정보)
https://trac.ffmpeg.org/wiki/StreamingGuide (Cygwin에서 ffserver가 빌드 됨)
https://ffmpeg.org/index.html#news (다음버전 부터는 ffserver는 drop 됨)
http://stackoverflow.com/a/37443771 (에러 패치 참고내용)