многопоточность и opengl

Автор wild_blood, 21 октября 2013, 15:16:58

« назад - далее »

0 Пользователи и 2 гостей просматривают эту тему.

wild_blood

Здравствуйте.
1 вопрос
есть 2 потока
как передать определенный програмистом сигнал из первого второму
чтобы тот остановился обработал сигнал и продолжил работать дальше
2 вопрос
и вот программа использующая sdl и opengl

Открыть содержимое (спойлер)

#include<iostream>
#include<math.h>
#include<SDL2/SDL.h>
#include"gl3w.h"

using namespace std;

void mult(float [], float []);
void  rotate(float , float , float , float  , float []);
void perspective(float , float , float , float  , float []);
GLuint shader();
int main()
{

  SDL_Event event;//keyboard event
  const Uint8* keys;//key
  int mouse1;
  int mouse2;
  float sh(0.1);
  GLfloat one = 1.0f;
  GLfloat black[4] = {0.0 , 0.0 , 0.0 , 1.0};
  GLuint render;
  GLuint array;
  GLuint buffer;
  //////////////////////////////////////
  GLfloat m_matrix[16] = {1.0 , 0.0 , 0.0 , 0.0,
  0.0 , 1.0 , 0.0 , 0.0,
  0.0 , 0.0 , 1.0 , 0.0,
  0.0 , 0.0 , 0.0 , 1.0,};

  GLfloat v_matrix[16] = {1.0 , 0.0 , 0.0 , 0.0,
  0.0 , 1.0 , 0.0 , 0.0,
  0.0 , 0.0 , 1.0 , 0.0,
  0.0 , 0.0 , 0.0 , 1.0,};

  GLfloat vs_matrix[16] = {1.0 , 0.0 , 0.0 , 0.0,
   0.0 , 1.0 , 0.0 , 0.0,
   0.0 , 0.0 , 1.0 , 0.0,
   0.0 , 0.0 , 0.0 , 1.0,};

  GLfloat proj_matrix[16] = {1.0 , 0.0 , 0.0 , 0.0,
     0.0 , 1.0 , 0.0 , 0.0,
     0.0 , 0.0 , 1.0 , 0.0,
     0.0 , 0.0 , 0.0 , 1.0,};

  GLfloat rot[16] = {1.0 , 0.0 , 0.0 , 0.0,
     0.0 , 1.0 , 0.0 , 0.0,
     0.0 , 0.0 , 1.0 , 0.0,
     0.0 , 0.0 , 0.0 , 1.0,};

  GLfloat m1_matrix[16] = {1.0 , 0.0 , 0.0 , 0.0,
   0.0 , 1.0 , 0.0 , 0.0,
   0.0 , 0.0 , 1.0 , 0.0,
   0.0 , 0.0 , 0.0 , 1.0,};
  perspective(90.0f , (float)640 / 480 , 0.1f , 1000 , proj_matrix);
  //////////////////////////////////////


  //////////////////////////////////////
  struct triangle
  {
    float model[16];                                                                           
    float coord[420][4];                                       
    float color[420][4];                                       
  };

  triangle t1;
  for(int i = 0 ; i < 16 ; i++)
    {
      if(i % 5 == 0) t1.model[i] = 1;
      else t1.model[i] = 0;
    }

  for(int i = 0; i < 420 ; i++)
    {
      for(int j = 0; j < 4 ; j++)
{
  t1.coord[i][j] = t1.color[i][j] = 0;
}
    }

  float g(0);
  for(int i = 0 , rc = 0 ; i < 70 ; i++ , rc += 6)
    {
      t1.coord[0 + rc][0] = -0.9 +g;
      t1.coord[0 + rc][1] = -0.9;
      t1.coord[0 + rc][2] = -1.5;
      t1.coord[0 + rc][3] = 1.0;

      t1.coord[1 + rc][0] = 0.85 + g;
      t1.coord[1 + rc][1] = -0.9;
      t1.coord[1 + rc][2] = -1.5;
      t1.coord[1 + rc][3] = 1.0;

      t1.coord[2 + rc][0] = -0.9 + g;
      t1.coord[2 + rc][1] = 0.85;
      t1.coord[2 + rc][2] = -1.5;
      t1.coord[2 + rc][3] = 1.0;

      t1.coord[3 + rc][0] = 0.9 + g;
      t1.coord[3 + rc][1] = -0.85;
      t1.coord[3 + rc][2] = -1.5;
      t1.coord[3 + rc][3] = 1.0;

      t1.coord[4 + rc][0] = 0.9 + g;
      t1.coord[4 + rc][1] = 0.9;
      t1.coord[4 + rc][2] = -1.5;
      t1.coord[4 + rc][3] = 1.0;

      t1.coord[5 + rc][0] = -0.85 + g;
      t1.coord[5 + rc][1] = 0.9;
      t1.coord[5 + rc][2] = -1.5;
      t1.coord[5 + rc][3] = 1.0;

      t1.color[0 + rc][0] = 1.0;
      t1.color[0 + rc][3] = 1.0;

      t1.color[1 + rc][1] = 1.0;
      t1.color[1 + rc][3] = 1.0;

      t1.color[2 + rc][2] = 1.0;
      t1.color[2 + rc][3] = 1.0;

      t1.color[3 + rc][0] = 1.0;
      t1.color[3 + rc][3] = 1.0;

      t1.color[4 + rc][1] = 1.0;
      t1.color[4 + rc][3] = 1.0;

      t1.color[5 + rc][2] = 1.0;
      t1.color[5 + rc][3] = 1.0;

      g += 1.9;
    }

  //////////////////////////////////////////
  if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
      cout<<"Error while initializing SDL "<<SDL_GetError()<<endl;
      return 0;
    }

  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);


  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);   
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);


  SDL_Window *window = SDL_CreateWindow("SDL_DEMO", 0, 0, 640, 480,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
  SDL_GLContext glcontext = SDL_GL_CreateContext(window);
  gl3wInit();


  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  glGenVertexArrays(1 , &array);
  glBindVertexArray(array);
  render = shader();

  rotate(90.0f , 1.0f , 0.0 , 0.0 , rot);
  mult(t1.model , rot);


  ///////////////////////////////////////////////
  glUseProgram(render);
  glGenBuffers(1, &buffer);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
  glBufferData(GL_SHADER_STORAGE_BUFFER , sizeof(t1), &t1, GL_DYNAMIC_DRAW);
  glBindBufferBase(GL_SHADER_STORAGE_BUFFER , 0 , buffer);
  ///////////////////////////////////////////////

  for(;;)
    {
      SDL_PollEvent(&event);
      {
keys = SDL_GetKeyboardState(NULL);
if(event.type == SDL_QUIT) break;
if(keys[SDL_SCANCODE_W])
  {
    m_matrix[12] += (v_matrix[8] * sh);
    m_matrix[13] += (v_matrix[9] * sh);
    m_matrix[14] += (v_matrix[10] * sh);
  }
if(keys[SDL_SCANCODE_S])
  {
    m_matrix[12] -= (v_matrix[8] * sh);
    m_matrix[13] -= (v_matrix[9] * sh);
    m_matrix[14] -= (v_matrix[10] * sh);
  }
if(keys[SDL_SCANCODE_A])
  {
    m_matrix[12] += (v_matrix[0] * sh);
    m_matrix[13] += (v_matrix[1] * sh);
    m_matrix[14] += (v_matrix[2] * sh);
  }
if(keys[SDL_SCANCODE_D])
  {
    m_matrix[12] -= (v_matrix[0] * sh);
    m_matrix[13] -= (v_matrix[1] * sh);
    m_matrix[14] -= (v_matrix[2] * sh);
  }
if(keys[SDL_SCANCODE_I])
  {
    sh += 0.001;
  }
if(keys[SDL_SCANCODE_O])
  {
    sh -= 0.001;
    if(sh < 0) sh = 0.01;
  }
if(keys[SDL_SCANCODE_Q])
  {
    rotate(1.0f , 0.0f , 0.0 , 1.0 , rot);
    mult(v_matrix , rot);
  }
if(keys[SDL_SCANCODE_E])
  {
    rotate(-1.0f , 0.0f , 0.0 , 1.0 , rot);
    mult(v_matrix , rot);
  }
SDL_GetRelativeMouseState(&mouse1 , &mouse2);
if(mouse1 > 0)
  {
    rotate(-1.0f , 0.0f , 1.0 , 0.0 , rot);
    mult(v_matrix , rot);
  }
if(mouse1 < 0)
  {
    rotate(1.0f , 0.0f , 1.0 , 0.0 , rot);
    mult(v_matrix , rot);
  }

if(mouse2 > 0)
  {
    rotate(-1.0f , 1.0f , 0.0 , 0.0 , rot);
    mult(v_matrix , rot);
  }
if(mouse2 < 0)
  {
    rotate(1.0f , 1.0f , 0.0 , 0.0 , rot);
    mult(v_matrix , rot);
  }
      }
      vs_matrix[0] = v_matrix[0];
      vs_matrix[1] = v_matrix[4];
      vs_matrix[2] = v_matrix[8];
      vs_matrix[3] = 0;

      vs_matrix[4] = v_matrix[1];
      vs_matrix[5] = v_matrix[5];
      vs_matrix[6] = v_matrix[9];
      vs_matrix[7] = 0;

      vs_matrix[8] = v_matrix[2];
      vs_matrix[9] = v_matrix[6];
      vs_matrix[10] = v_matrix[10];
      vs_matrix[11] = 0;

      vs_matrix[12] = v_matrix[12];
      vs_matrix[13] = v_matrix[13];
      vs_matrix[14] = v_matrix[14];
      vs_matrix[15] = v_matrix[15];


      glClearBufferfv(GL_COLOR, 0, black);
      glClearBufferfv(GL_DEPTH, 0, &one);

      glUniformMatrix4fv(1 , 1 , GL_FALSE , m_matrix);
      glUniformMatrix4fv(2 , 1 , GL_FALSE , vs_matrix);
      glUniformMatrix4fv(3 , 1 , GL_FALSE , proj_matrix);
     
      for(int i = 0 ; i < 16 ; i++) m1_matrix[i] = m_matrix[i];
   
      for(int i = 0 ; i < 200 ; i++)
{
  glDrawArrays(GL_TRIANGLES , 0 , 420);
  m1_matrix[14] += 2;
  glUniformMatrix4fv(1 , 1 , GL_FALSE , m1_matrix);
}

      SDL_GL_SwapWindow(window);
    }

  glDeleteVertexArrays(1 , &array);
  glDeleteProgram(render);
  SDL_GL_DeleteContext(glcontext);
  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}



GLuint shader(void)
{
  GLuint program;

  static const GLchar * vertex_shader_source[] =
    {
     
      "#version 430 core                                                                                                                                \n"
      "layout (location = 1) uniform mat4 m_matrix;                                                                                                     \n"
      "layout (location = 2) uniform mat4 v_matrix;                                                                                                     \n"
      "layout (location = 3) uniform mat4 proj_matrix;                                                                                                  \n"
      "layout (binding = 0 , std430) buffer block                                                                                                       \n"
      "{                                                                                                                                                \n"
      "float model[16];                                                                                                                                 \n"
      "float coord[420][4];                                                                                                                               \n"
      "float color[420][4];                                                                                                                               \n"
      "} c1;                                                                                                                                            \n"
      "out vec4 vs_color;                                                                                                                               \n"
      "void main(void)                                                                                                                                  \n"
      "{                                                                                                                                                \n"
      "mat4 model;                                                                                                                                      \n"
      "int i = 0;                                                                                                                                       \n"
      "for(int k = 0 ; k < 4 ; k++)                                                                                                                     \n"
      "{                                                                                                                                                \n"
      "for(int g = 0 ; g < 4 ; g++ , i++)                                                                                                               \n"
      "{                                                                                                                                                \n"
      "model[k][g] = c1.model[i];                                                                                                                       \n"
      "}                                                                                                                                                \n"
      "}                                                                                                                                                \n"
      "                                                                                                                                                 \n"
      "vec4 vertices = vec4(c1.coord[gl_VertexID][0] , c1.coord[gl_VertexID][1] , c1.coord[gl_VertexID][2] , c1.coord[gl_VertexID][3]);                 \n"
      "vec4 color = vec4(c1.color[gl_VertexID][0] , c1.color[gl_VertexID][1] , c1.color[gl_VertexID][2] , c1.color[gl_VertexID][3]);                    \n"
      "gl_Position = proj_matrix * v_matrix * (m_matrix * model) * vertices;                                                                            \n"
      "vs_color = color;                                                                                                                                \n"
      "}                                                                                                                                                \n"
     
    };

  static const GLchar * fragment_shader_source[] =
    {
     
      "#version 430 core                             \n"
      "out vec4 color;                               \n"
      "in vec4 vs_color;                             \n"
      "void main(void)                               \n"
      "{                                             \n"
      "color = vs_color;                             \n"
      "}                                             \n"
     
    };


  program = glCreateProgram();
  GLuint vs = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vs, 1, vertex_shader_source, NULL);
  glCompileShader(vs);
 
  GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fs, 1, fragment_shader_source, NULL);
  glCompileShader(fs);

  glAttachShader(program, vs);
  glAttachShader(program, fs);


  glLinkProgram(program);

  glDeleteShader(vs);
  glDeleteShader(fs);

  return program;
}
void mult(float cx[16], float cy[16])
{
float rx[16];
  rx[0]     = cx[0] * cy[0] + cx[4] * cy[1] + cx[8] * cy[2] + cx[12] * cy[3];
  rx[4]     = cx[0] * cy[4] + cx[4] * cy[5] + cx[8] * cy[6] + cx[12] * cy[7];
  rx[8]     = cx[0] * cy[8] + cx[4] * cy[9] + cx[8] * cy[10] + cx[12] * cy[11];
  rx[12]    = cx[0] * cy[12] + cx[4] * cy[13] + cx[8] * cy[14] + cx[12] * cy[15];
       
  rx[1]     = cx[1] * cy[0] + cx[5] * cy[1] + cx[9] * cy[2] + cx[13] * cy[3];
  rx[5]     = cx[1] * cy[4] + cx[5] * cy[5] + cx[9] * cy[6] + cx[13] * cy[7];
  rx[9]     = cx[1] * cy[8] + cx[5] * cy[9] + cx[9] * cy[10] + cx[13] * cy[11];
  rx[13]    = cx[1] * cy[12] + cx[5] * cy[13] + cx[9] * cy[14] + cx[13] * cy[15];
       
  rx[2]     = cx[2] * cy[0] + cx[6] * cy[1] + cx[10] * cy[2] + cx[14] * cy[3];
  rx[6]     = cx[2] * cy[4] + cx[6] * cy[5] + cx[10] * cy[6] + cx[14] * cy[7];
  rx[10]    = cx[2] * cy[8] + cx[6] * cy[9] + cx[10] * cy[10] + cx[14] * cy[11];
  rx[14]    = cx[2] * cy[12] + cx[6] * cy[13] + cx[10] * cy[14] + cx[14] * cy[15];
       
  rx[3]     = cx[3] * cy[0] + cx[7] * cy[1] + cx[11] * cy[2] + cx[15] * cy[3];
  rx[7]     = cx[3] * cy[4] + cx[7] * cy[5] + cx[11] * cy[6] + cx[15] * cy[7];
  rx[11]    = cx[3] * cy[8] + cx[7] * cy[9] + cx[11] * cy[10] + cx[15] * cy[11];
  rx[15]    = cx[3] * cy[12] + cx[7] * cy[13] + cx[11] * cy[14] + cx[15] * cy[15];

  for(int i = 0 ; i < 16; i++) cx[i] = rx[i]; 
}

void  rotate(float angle, float x, float y, float z , float m[16])
{
  float x2 = x * x;
  float y2 = y * y;
  float z2 = z * z;
  float rads = float(angle) * 0.0174532925f;
  float c = cosf(rads);
  float s = sinf(rads);
  float omc = 1.0f - c;

  m[0] = (x2 * omc + c);
  m[1] = (y * x * omc + z * s);
  m[2] = (x * z * omc - y * s);
  m[3] = 0;

  m[4] = (x * y * omc - z * s);
  m[5] = (y2 * omc + c);
  m[6] = (y * z * omc + x * s);
  m[7] = 0;

  m[8] = (x * z * omc + y * s);
  m[9] = (y * z * omc - x * s);
  m[10] = (z2 * omc + c);
  m[11] = 0;

}

void perspective(float fovy, float aspect, float n, float f , float result[16])
{
  float q = 1.0f / tan((0.5f * fovy) * (M_PI/180.0));
  float A = q / aspect;
  float B = (n + f) / (n - f);
  float C = (2.0f * n * f) / (n - f);

  result[0] = A;
  result[1] = 0.0f;
  result[2] = 0.0f;
  result[3] = 0.0f;

  result[4] = 0.0f;
  result[5] = q;
  result[6] = 0.0f;
  result[7] = 0.0f;

  result[8] = 0.0f;
  result[9] = 0.0f;
  result[10] = B;
  result[11] = -1.0f;

  result[12] = 0.0f;
  result[13] = 0.0f;
  result[14] = C;
  result[15] = 0.0f;
}

[свернуть]
почему если значение переменной i увеличить до скажем тыщи в этом цикле
      for(int i = 0 ; i < 200 ; i++)
   {
     glDrawArrays(GL_TRIANGLES , 0 , 420);
     m1_matrix[14] += 2;
     glUniformMatrix4fv(1 , 1 , GL_FALSE , m1_matrix);
   }
она начинает дико лагать
у меня hd 6970 ,  i7 3770 , 8gb ram
спасибо

smallNix

 Имеются в виду потоки POSIX или процессы, созданные через fork() (vfork, posix_spawn). Можешь использовать сигналы. В одном потоке генерируешь сигнал (например, kill()), а в другом у тебя должен быть обработчик сигнала.
Кто-то же должен что-то делать...

Olej

#2
Цитата: smallNix от 07 ноября 2013, 20:52:30
Имеются в виду потоки POSIX или процессы, созданные через fork() (vfork, posix_spawn). Можешь использовать сигналы. В одном потоке генерируешь сигнал (например, kill()), а в другом у тебя должен быть обработчик сигнала.

Ничего подобного!
Сигнал UNIX в многопоточных приложениях направляется не потоку, а процессу в целом, а обрабатывается одним из потоков (каким - непредсказуемо), обработчик сигнала к потоку не привязывается.
... но это может управляться сигнальной маской, которая устанавливается для потоков раздельно.


Сообщение объединено: 09 ноября 2013, 02:23:36

Цитата: wild_blood от 21 октября 2013, 15:16:58
как передать определенный програмистом сигнал из первого второму

Про сигналы в мнгопоточной среде можете почитать (с примерами кода) здесь: Инструменты Linux, стр. там примерно 140 и далее...

sunny_side

С точки зрения реализуемости, используя pthread_kill можно реализовать схему:
------------------------------------------------------------------------------------------
есть 2 потока, передать определенный програмистом сигнал из первого второму
чтобы тот остановился обработал сигнал и продолжил работать дальше
------------------------------------------------------------------------------------------
http://stackoverflow.com/questions/6398084/using-sigusr1-and-sigusr2-as-signals-in-pthread-kill

С точки зрения удобства, подход используемый в d language наиболее прозрачен для дальнейшего расширения, изменения:
http://www.informit.com/articles/article.aspx?p=1609144&seqNum=5

Что-то похожее можно реализовать в c:
http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch12lev1sec8.html 

Также нужно принимать во внимание неудобство отладки асинхронно взаимодействующих потоков.
В данном случае, наверное, лучше использовать синхронное взаимодействие потоков.

smallNix

#4
ЦитироватьНичего подобного!
Сигнал UNIX в многопоточных приложениях направляется не потоку, а процессу в целом, а обрабатывается одним из потоков (каким - непредсказуемо), обработчик сигнала к потоку не привязывается.
... но это может управляться сигнальной маской, которая устанавливается для потоков раздельно.

Вообще-то я спросил, что используется   :D Если процессы, то мой совет с сигналами (не надо никаких масок) очень даже применим. Именно к этому случаю мой пост относился. А если потоки, тогда можно использовать pthread_cancel (pthread_t thrNum); Кроме того, следует (если надо) назначить обработчик завершения (что-то вроде деструктора  ;D ), прерываемого потока (нам же ненужны утечки памяти). Поможет pthread_cleanup_push. И очень важно - надо просмотреть прерываемый поток, что прикинуть в каких местах он может завершиться. Спецификация SUS описывает что-то около 200 функций, которые могут эту роль выполнять (Я не читал, поэтому поверхностно представляю какие именно). Зато, на всякий случай, если подозреваешь, что у тебя в потоке может не оказаться точек завершения, можно самому определить их (точки завершения) через pthread_testcancel 
Кто-то же должен что-то делать...

Olej

#5
Цитата: sunny_side от 09 ноября 2013, 17:50:50
Что-то похожее можно реализовать в c:
http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch12lev1sec8.html 

Это и есть то, что я назвал игрищами с сигнальными масками, которые для потоков будут различными.



Сообщение объединено: 09 ноября 2013, 21:52:41

Цитата: smallNix от 09 ноября 2013, 21:34:46
(Я не читал, поэтому поверхностно представляю какие именно).

:)
Если кому-то это будет в пользу, то по-быстренькому (но подробно) можете почитать это здесь: QNX/UNIX: анатомия параллелизма (есть во многих местах в Интернет для свободного скачивания).



smallNix

Olej,
Цитата: Olej от 09 ноября 2013, 21:48:28:)
Если кому-то это будет в пользу, то по-быстренькому (но подробно) можете почитать это здесь: QNX/UNIX: анатомия параллелизма (есть во многих местах в Интернет для свободного скачивания).
Спасибо. Обязательно ознакомлюсь.
Кто-то же должен что-то делать...

sunny_side

Цитата: Olej от 09 ноября 2013, 21:48:28игрищами с сигнальными масками

Вспомнился подход используемый в beos/haiku - http://www.haiku-os.org/legacy-docs/bebook/TheApplicationKit_Messaging.html позволяющий упростить взаимодействие между потоками и процессами и унифицировать межпроцессное и межпотоковое взаимодействие. Все же unix/linux содержит довольно неудобное в использовании наследие 70-х :)

Кстати подход к ipc из qnx даже портировали в линукс - http://en.wikipedia.org/wiki/SIMPL, хотя насколько хорошо он выдерживает нагрузки... это еще вопрос.   

Olej

Цитата: sunny_side от 09 ноября 2013, 23:30:09
Кстати подход к ipc из qnx даже портировали в линукс - http://en.wikipedia.org/wiki/SIMPL, хотя насколько хорошо он выдерживает нагрузки... это еще вопрос.   

Это только имитация (моделирование) синхронного обмена сообщениями, для микроядерных ОС (QNX) - он естественный, базовый, низкоуровневый. В моноядерной ОС (Linux ... и все остальные :)) он может только моделироваться, и, естественно, с очень большими накладными расходами.


sunny_side

в плане новых идей интересна genode(http://genode.org/). они даже пытаються запустить linux поверх своего ядра.

qnx, да, хорош, если его применять по назначению.
он кстати, используется в BostonDynamics(http://en.wikipedia.org/wiki/BigDog).
но для других сфер - он приносит больше проблем :( - производительность(qnx гордится fast ipc в своих рекламных акциях, но это лишь реклама)
- так себе, ужасная поддерка, ну и цена + баги, которые фиксаться годами.
у них когда-то была инициатива по открытию исходников, но она, к сожалению, не увенчалась успехом, а могла принести многое.

у xnu(macosx) довольно удачное решение позволяющее сохранить преимущества ipc ориентированного на сообщения и не затачивать ос лишь под одну сферу применения - http://ru.wikipedia.org/wiki/XNU.

Olej

Цитата: sunny_side от 10 ноября 2013, 15:58:16
qnx, да, хорош, если его применять по назначению.
...
у них когда-то была инициатива по открытию исходников, но она, к сожалению, не увенчалась успехом, а могла принести многое.

Про QNX забудьте и название :).
После нескольких последовательных перепродаж фирмы и распродажи с молотка нынешнего владельца RIM & BlackBerry - от QNX (переименованного в Blackberry 10) ничего не осталось!
(после последней покупки 2007г. RIM и закрыли открытую уже к тому времени почти полностью QNX)