Сообщество программистов Autodesk в СНГ

ADN Club => ObjectARX => Тема начата: lom0v от 26-03-2019, 04:43:59

Название: Объект не закрывется при фиксации транзакции
Отправлено: lom0v от 26-03-2019, 04:43:59
Объект открывается через механизм транзакций дважды в разных режимах. Транзакции вложенные. Для некоторых сочетаний режимов открытия объекта внешняя транзакция при фиксации не закрывает объект.

Это я что-то делаю неправильно или это баг Автокада? (Проверял на 2015 и на 2017.)

Ниже код функции, демонстрирующей эффект.

Сначала создаю объект AcDbLine и вставляю его в базу. Затем под первой транзакцией открываю объект в режиме AcDb::kForWrite, а под второй - в режиме AcDb::kForNotify и закрываю обе транзакции с фиксацией. После этого пытаюсь удалить объект.

Функция выводит в лог:
ErrorStatus = 83
--- Read enabled
--- Write enabled

Т.е. при попытке открыть объект на запись возвращается eWasOpenForWrite (код 83), а дальнейшее копание через указатель на объект показывает, что он открыт и на запись, и на чтение. (Конечно возможно, что объект по указтелю уже попорчен, поскольку объект вроде должен быть закрыт при закрытии внешней транзакции. Но если это так, вообще непонятно как выкручиваться.) После явного вызова close() объект наконец закрывается и вторая попытка открыть объект на запись с последующим удалением проходит успешно. И это, с учётом того, что у меня есть какой-никакой указатель на объект. А если весь этот демонстрационный код раскидан по разным функциям/методам/классам, как это есть в реальности?


Код - C++ [Выбрать]
  1. void Test_Trans() {
  2.  
  3.         AcDbObjectId id;
  4.         AcDbObject *pObj1 = nullptr, *pObj2 = nullptr;
  5.         Acad::ErrorStatus es;
  6.  
  7.         // создать объект AcDbLine с фиксированными координатами начала и конца
  8.         AcDbDatabase *pCurDb = acdbHostApplicationServices()->workingDatabase();
  9.         AcDbBlockTable *pBlockTable = nullptr;
  10.         pCurDb->getSymbolTable(pBlockTable, AcDb::kForRead);
  11.         AcDbBlockTableRecord *pModelSpace = nullptr;
  12.         pBlockTable->getAt(ACDB_MODEL_SPACE, pModelSpace, AcDb::kForWrite);
  13.         pBlockTable->close();
  14.         AcGePoint3d startPt(0.0, 0.0, 0.0), endPt(2000.0, 1000.0, 0.0);
  15.         AcDbLine *pLine = new AcDbLine(startPt, endPt);
  16.         pModelSpace->appendAcDbEntity(id, pLine);
  17.         pLine->close();
  18.         pModelSpace->close();
  19.  
  20.         // открытие объекта через транзакции
  21.         AcTransaction *pTrans1 = actrTransactionManager->startTransaction();
  22.  
  23.         es = pTrans1->getObject(pObj1, id, AcDb::kForWrite, false);
  24.         if (es != Acad::eOk)
  25.                 acutPrintf(L"pTrans1->getObject() ErrorStatus = %d\n", (int)es);
  26.  
  27.         AcTransaction *pTrans2 = actrTransactionManager->startTransaction();
  28.  
  29.         es = pTrans2->getObject(pObj2, id, AcDb::kForNotify, false);
  30.         if (es != Acad::eOk)
  31.                 acutPrintf(L"pTrans2->getObject() ErrorStatus = %d\n", (int)es);
  32.  
  33.         actrTransactionManager->endTransaction();
  34.         actrTransactionManager->endTransaction();
  35.  
  36.         AcDbEntity *pEnt = NULL;
  37.         // первая попытка прибить объект
  38.         es = acdbOpenObject(pEnt, id, AcDb::OpenMode::kForWrite);
  39.         if (pEnt && es == Acad::eOk) {
  40.                 pEnt->erase(Adesk::kTrue);
  41.                 pEnt->close();
  42.         }
  43.         else {
  44.                 acutPrintf(L"ErrorStatus = %d\n", (int)es);
  45.  
  46.                 if (pObj1 && id.isResident()) {
  47.                         bool needFix = false;
  48.                         if (pObj1->isReadEnabled()) {
  49.                                 acutPrintf(L"--- Read enabled\n");
  50.                                 needFix = true;
  51.                         }
  52.                         if (pObj1->isWriteEnabled()) {
  53.                                 acutPrintf(L"--- Write enabled\n");
  54.                                 needFix = true;
  55.                         }
  56.                         if (pObj1->isNotifyEnabled()) {
  57.                                 acutPrintf(L"--- Notify enabled\n");
  58.                                 needFix = true;
  59.                         }
  60.                         if (needFix)
  61.                                 pObj1->close();
  62.  
  63.                         // вторая попытка прибить объект
  64.                         es = acdbOpenObject(pEnt, id, AcDb::OpenMode::kForWrite);
  65.                         if (pEnt && es == Acad::eOk) {
  66.                                 pEnt->erase(Adesk::kTrue);
  67.                                 pEnt->close();
  68.                         }
  69.                         else
  70.                                 acutPrintf(L"ErrorStatus = %d\n", (int)es);
  71.                 }
  72.         }
  73. }
  74.  
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: Александр Ривилис от 26-03-2019, 12:06:05
Это я что-то делаю неправильно или это баг Автокада? (Проверял на 2015 и на 2017.)
Это не баг. Причина в том, что actrTransactionManager->endTransaction() не приводит к немедленному закрытию вовлеченных в транзакцию объектов в отличие от pObj->close(). Почитай в документации: https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2017/ENU/OARXMAC-DevGuide/files/GUID-F33EB1D7-6C92-4A77-A061-2001B4A390BA-htm.html
И вообще смешение двух вариантов работы с объектами (с транзакцией и без) чревато очень неприятными ошибками.
Я предпочитаю вообще не пользоваться транзакциями.
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: lom0v от 26-03-2019, 15:11:28
Т.е. actrTransactionManager->endTransaction() работает асинхронно. Тогда, исходя из ваших слов и
Цитировать
You can modify individual objects after each has been committed, but it is recommended that you cache the IDs of the objects you want to modify and wait until you receive the transactionEnded() notification signaling the end of all the transactions, then do the modifications.
я не совсем понимаю, как использовать транзакции в синхронном коде, как в моём примере.

Получается, что в районе строки 35 должна быть какая-то задержка до момента окончания закрытия транзакции. Вызов actrTransactionManager->numActiveTransactions() сразу после закрытия внешней транзакции возвращает 0, следовательно его использовать нельзя. Исходя из вышеприведённой цитаты из документации, надо делать отдельный класс, унаследованный от AcTransactionReactor, переопределять в нём transactionEnded(), цеплять к менеджеру транзакций. А в строке 35 в цикле ждать, пока не отработает реактор и не выставит флаг, что транзакция полностью завершилась. Так? Или есть способ попроще?
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: Александр Ривилис от 26-03-2019, 15:18:12
Так? Или есть способ попроще?
Попроще только не использовать транзакции (тем более вложенные).
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: lom0v от 26-03-2019, 15:34:41
Попроще только не использовать транзакции (тем более вложенные).
Тоже не от меня зависит.
Спасибо за ответы.
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: Александр Ривилис от 26-03-2019, 16:18:27
lom0v,
Я немного подрихтовал твой код с использованием интелектуальных указателей (2 -го вида):
Код - C++ [Выбрать]
  1. static void MyGroupTestTrans() {
  2.   AcDbObjectId id;
  3.   AcDbObject *pObj1 = nullptr, *pObj2 = nullptr;
  4.   Acad::ErrorStatus es;
  5.  
  6.   // создать объект AcDbLine с фиксированными координатами начала и конца
  7.   AcDbDatabase *pCurDb = acdbHostApplicationServices()->workingDatabase();
  8.   AcDbBlockTable *pBlockTable = nullptr;
  9.   pCurDb->getSymbolTable(pBlockTable, AcDb::kForRead);
  10.   AcDbBlockTableRecord *pModelSpace = nullptr;
  11.   pBlockTable->getAt(ACDB_MODEL_SPACE, pModelSpace, AcDb::kForWrite);
  12.   pBlockTable->close();
  13.   AcGePoint3d startPt(0.0, 0.0, 0.0), endPt(2000.0, 1000.0, 0.0);
  14.   AcDbLine *pLine = new AcDbLine(startPt, endPt);
  15.   pModelSpace->appendAcDbEntity(id, pLine);
  16.   pLine->close();
  17.   pModelSpace->close();
  18.  
  19.   // открытие объекта через транзакции
  20.   AcTransaction *pTrans1 = actrTransactionManager->startTransaction();
  21.  
  22.   es = pTrans1->getObject(pObj1, id, AcDb::kForWrite, false);
  23.   if (es != Acad::eOk)
  24.     acutPrintf(L"pTrans1->getObject() ErrorStatus = %s\n", acadErrorStatusText(es));
  25.  
  26.   AcTransaction *pTrans2 = actrTransactionManager->startTransaction();
  27.  
  28.   es = pTrans2->getObject(pObj2, id, AcDb::kForNotify, false);
  29.   if (es != Acad::eOk)
  30.     acutPrintf(L"pTrans2->getObject() ErrorStatus = %s\n", acadErrorStatusText(es));
  31.  
  32.   es = actrTransactionManager->endTransaction();
  33.   es = actrTransactionManager->endTransaction();
  34.  
  35.   //////////////////////////////////////////////////////////////////////////
  36.   //  Используем новые интеллектуальные указатели
  37.   //////////////////////////////////////////////////////////////////////////
  38.   AcDbSmartObjectPointer<AcDbEntity> pEnt(id, AcDb::OpenMode::kForWrite);
  39.   if ((es = pEnt.openStatus()) == Acad::eOk)
  40.   {
  41.     pEnt->erase(Adesk::kTrue);
  42.   }
  43.   else
  44.   {
  45.     acutPrintf(L"pEnt.openStatus() ErrorStatus = %s\n", acadErrorStatusText(es));
  46.   }
  47. }

И судя по всему всё нормально работает.
Название: Re: Объект не закрывется при фиксации транзакции
Отправлено: lom0v от 26-03-2019, 19:25:19
Да, с этим работает. Спасибо!